Files
AutoGPT-Code-Ability/codex/develop/compile.py
T

167 lines
5.2 KiB
Python

import ast
import logging
from typing import List
from prisma.models import (
APIRouteSpec,
CompiledRoute,
CompletedApp,
Function,
Package,
Specification,
)
from prisma.types import CompiledRouteCreateInput, CompletedAppCreateInput
from pydantic import BaseModel
from codex.api_model import Identifiers
logger = logging.getLogger(__name__)
class CompiledFunction(BaseModel):
packages: List[Package]
imports: List[str]
code: str
async def compile_route(
ids: Identifiers, route_root_func: Function, api_route: APIRouteSpec
) -> CompiledRoute:
"""
Compiles a route by generating a CompiledRoute object.
Args:
ids (Identifiers): The identifiers used in the route.
route_root_func (Function): The root function of the route.
api_route (APIRouteSpec): The specification of the API route.
Returns:
CompiledRoute: The compiled route object.
"""
compiled_function = await recursive_compile_route(route_root_func)
unique_packages = list(set([package.id for package in compiled_function.packages]))
code = "\n".join(compiled_function.imports)
code += "\n\n"
code += compiled_function.code
data = CompiledRouteCreateInput(
description=api_route.description,
Packages={"connect": [{"id": package_id} for package_id in unique_packages]},
fileName=api_route.functionName + "_service.py",
mainFunctionName=route_root_func.functionName,
compiledCode=code,
RootFunction={"connect": {"id": route_root_func.id}},
ApiRouteSpec={"connect": {"id": api_route.id}},
)
compiled_route = await CompiledRoute.prisma().create(data)
return compiled_route
async def recursive_compile_route(in_function: Function) -> CompiledFunction:
"""
Recursively compiles a function and its child functions
into a single CompiledFunction object.
Args:
ids (Identifiers): The identifiers for the function.
function (Function): The function to compile.
Returns:
CompiledFunction: The compiled function.
Raises:
ValueError: If the function code is missing.
"""
# Can't see how to do recursive lookup with prisma, so I'm checking the next
# layer down each time. This is a bit of a hack, can be improved later.
function = await Function.prisma().find_unique_or_raise(
where={"id": in_function.id},
include={
"ParentFunction": True,
"ChildFunction": {"include": {"ApiRouteSpec": True}},
},
)
logger.info(f"Compiling function: {function.id}")
if function.ChildFunction is None:
packages = []
if function.Packages:
packages = function.Packages
if function.functionCode is None:
raise ValueError(f"Leaf Function code is required! {function.id}")
code = "\n".join(function.importStatements)
code += "\n\n"
code += function.functionCode
try:
tree = ast.parse(code)
except Exception as e:
raise ValueError(f"Syntax error in function code: {e}, {code}")
return CompiledFunction(
packages=packages,
imports=function.importStatements,
code=function.functionCode,
)
else:
packages = []
imports = []
code = ""
for child_function in function.ChildFunction:
compiled_function = await recursive_compile_route(child_function)
packages.extend(compiled_function.packages)
imports.extend(compiled_function.imports)
code += "\n\n"
code += compiled_function.code
if function.Packages:
packages.extend(function.Packages)
imports.extend(function.importStatements)
if function.functionCode is None:
raise ValueError(f"Function code is required! {function.id}")
code += "\n\n"
code += function.functionCode
check_code = "\n".join(imports)
check_code += "\n\n"
check_code += code
try:
tree = ast.parse(check_code)
except Exception as e:
raise ValueError(f"Syntax error in function code: {e}, {code}")
return CompiledFunction(packages=packages, imports=imports, code=code)
async def create_app(
ids: Identifiers, spec: Specification, compiled_routes: List[CompiledRoute]
) -> CompletedApp:
"""
Create an app using the given identifiers, specification, and compiled routes.
Args:
ids (Identifiers): The identifiers for the app.
spec (Specification): The specification for the app.
compiled_routes (List[CompiledRoute]): The compiled routes for the app.
Returns:
CompletedApp: The completed app object.
"""
if spec.ApiRouteSpecs is None:
raise ValueError("Specification must have at least one API route.")
data = CompletedAppCreateInput(
name=spec.name,
description=spec.context,
User={"connect": {"id": ids.user_id}},
CompiledRoutes={"connect": [{"id": route.id} for route in compiled_routes]},
Specification={"connect": {"id": spec.id}},
Application={"connect": {"id": ids.app_id}},
)
app = await CompletedApp.prisma().create(data)
return app