Files
Zamil Majdyandmajdyz 803b015f98 [AGPT-714] Implement inter-page linking on front-end page decomposition by prompting (#263)
## **User description**
Changes:
* Hint the UI page generation with its connection to another pages.
* Add validation on the page connection prompting
* Cleaner auto import on missing function and missing object (remove wildcard, import only the missing part). on missing object we try to find the related function and import the file that hosts that function (only support the object type referred by the ApiRouteSpec, it can still be improved).

Out of scope:
* Validation of ui.link and page connection (if LLM hallucinate the page on `ui.link`, we are not failing this yet!)


___

## **Type**
Enhancement, Bug fix


___

## **Description**
- Enhanced code validation to include checks for `ui.link` usage and ensure it references valid functions.
- Improved import handling in `__fix_missing_imports` by adding async fetching of functions related to object types and better error message integration.
- Added a new database function `get_object_type_referred_functions` to fetch functions related to an object type.
- Updated `PageDecompositionEntry` to use a dictionary for `used_functions` to include descriptions, and added `PageConnection` model for describing connections between pages.
- Enhanced validation in `PageDecompositionBlock` to include checks for main function naming, existence of a home page, and valid page connections.
- Updated Jinja2 template example to reflect new features in page decomposition, including detailed function usage and page connections.



___



## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>code_validation.py</strong><dd><code>Enhance Code Validation and Import Handling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>
      
codex/develop/code_validation.py

<li>Added validation for <code>ui.link</code> usage in function code to ensure it <br>references valid functions.<br> <li> Enhanced the <code>__fix_missing_imports</code> function to handle missing imports <br>more accurately, including async fetching of functions related to <br>object types.<br> <li> Improved error handling by appending problematic lines to error <br>messages.<br>


</details>
    

  </td>
  <td><a href="https://github.com/Significant-Gravitas/codex/pull/263/files#diff-deae541000e158a1fc146c31004d52ed52a858802dd058803d920d97477071d6">+35/-10</a>&nbsp; </td>

</tr>                    

<tr>
  <td>
    <details>
      <summary><strong>database.py</strong><dd><code>Add Database Function for Object Type Function Fetching</code>&nbsp; &nbsp; </dd></summary>
<hr>
      
codex/develop/database.py

<li>Added a new function <code>get_object_type_referred_functions</code> to fetch <br>functions related to an object type.<br>


</details>
    

  </td>
  <td><a href="https://github.com/Significant-Gravitas/codex/pull/263/files#diff-61c5f5198f842fcc472b6b31ee2e5a53eac489a88152cbc792895f1f9d60f694">+23/-1</a>&nbsp; &nbsp; </td>

</tr>                    

<tr>
  <td>
    <details>
      <summary><strong>ai_page_decompose.py</strong><dd><code>Enhance Page Decomposition with Detailed Function Usage and Page </code><br><code>Connections</code></dd></summary>
<hr>
      
codex/requirements/blocks/ai_page_decompose.py

<li>Updated <code>PageDecompositionEntry</code> to use a dictionary for <code>used_functions</code> <br>to include descriptions.<br> <li> Added <code>PageConnection</code> model to describe connections between pages.<br> <li> Enhanced validation in <code>PageDecompositionBlock</code> to include checks for <br>main function naming, existence of a home page, and valid page <br>connections.<br>


</details>
    

  </td>
  <td><a href="https://github.com/Significant-Gravitas/codex/pull/263/files#diff-0539cf8ae85457e8d87308e181529144d8f564ad2453eabecc509b4f02c4abe2">+55/-9</a>&nbsp; &nbsp; </td>

</tr>                    
</table></td></tr><tr><td><strong>Documentation
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>system.j2</strong><dd><code>Update Example to Demonstrate Enhanced Page Decomposition Features</code></dd></summary>
<hr>
      
codex/prompts/gpt-4-turbo/requirements/page_decompose/system.j2

<li>Modified the example to reflect changes in the <code>PageDecompositionEntry</code> <br>model using a dictionary for <code>used_functions</code>.<br> <li> Added <code>connections</code> field to demonstrate inter-page linking in the <br>example.<br>


</details>
    

  </td>
  <td><a href="https://github.com/Significant-Gravitas/codex/pull/263/files#diff-c081d45d8aa12144bab35cca95d5b45f35e7a380a84e71632b1d425d9a712f6c">+26/-11</a>&nbsp; </td>

</tr>                    
</table></td></tr></tr></tbody></table>

___

> ✨ **PR-Agent usage**:
>Comment `/help` on the PR to get a list of all available PR-Agent tools and their descriptions

---------

Co-authored-by: majdyz <[email protected]>
2024-05-03 16:26:31 +07:00

158 lines
5.0 KiB
Python

from typing import List, Tuple
from prisma.models import (
CompiledRoute,
CompletedApp,
Function,
ObjectType,
Specification,
)
from codex.api_model import Identifiers, Pagination
from codex.common.database import INCLUDE_API_ROUTE, INCLUDE_FUNC
async def get_deliverable(deliverable_id: str) -> CompletedApp:
completed_app = await CompletedApp.prisma().find_unique_or_raise(
where={"id": deliverable_id, "deleted": False},
include={
"CompiledRoutes": {
"include": {
"RootFunction": INCLUDE_FUNC, # type: ignore
"Functions": INCLUDE_FUNC,
"ApiRouteSpec": INCLUDE_API_ROUTE,
}
}
},
)
return completed_app
async def delete_deliverable(
user_id: str, app_id: str, spec_id: str, deliverable_id: str
) -> None:
await CompletedApp.prisma().update(
where={"id": deliverable_id},
data={"deleted": True},
)
async def list_deliverables(
user_id: str,
app_id: str,
spec_id: str,
page: int = 1,
page_size: int = 10,
) -> Tuple[List[CompletedApp], Pagination]:
skip = (page - 1) * page_size
total_items = await CompletedApp.prisma().count(
where={"deleted": False, "specificationId": spec_id}
)
if total_items == 0:
return [], Pagination(total_items=0, total_pages=0, current_page=0, page_size=0)
total_pages = (total_items + page_size - 1) // page_size
completed_apps_data = await CompletedApp.prisma().find_many(
skip=skip,
take=page_size,
include={"CompiledRoutes": True},
where={"deleted": False, "specificationId": spec_id},
)
pagination = Pagination(
total_items=total_items,
total_pages=total_pages,
current_page=page,
page_size=page_size,
)
return completed_apps_data, pagination
async def get_compiled_code(deliverable_id: str) -> list[str]:
routes = await CompiledRoute.prisma().find_many(
where={"completedAppId": deliverable_id}
)
return [route.compiledCode for route in routes]
async def get_compiled_route(compiled_route_id: str) -> CompiledRoute:
return await CompiledRoute.prisma().find_unique_or_raise(
where={"id": compiled_route_id},
include={
"RootFunction": INCLUDE_FUNC, # type: ignore
"Functions": INCLUDE_FUNC, # type: ignore
"ApiRouteSpec": INCLUDE_API_ROUTE, # type: ignore
},
)
async def get_ids_from_function_id_and_compiled_route(
function_id: str, compiled_route_id: str
) -> Identifiers:
function = await Function.prisma().find_unique_or_raise(
where={"id": function_id},
include={
"CompiledRoute": {
"include": {
"ApiRouteSpec": True,
"CompletedApp": {
"include": {
"User": True,
"Application": True,
"Specification": True,
},
},
},
},
},
)
compiled_route = await CompiledRoute.prisma().find_unique_or_raise(
where={"id": compiled_route_id},
include={
"CompletedApp": {
"include": {"User": True, "Application": True, "Specification": True},
}
},
)
if not compiled_route.CompletedApp:
raise ValueError("Compiled route does not have a completed app")
if not compiled_route.CompletedApp.Specification:
raise ValueError("Compiled route does not have a specification")
specification = await Specification.prisma().find_unique_or_raise(
where={"id": compiled_route.CompletedApp.Specification.id},
include={
"Interview": True,
"User": True,
},
)
if not specification.User:
raise ValueError("Specification does not have a user")
return Identifiers(
user_id=specification.userId,
cloud_services_id=specification.User.cloudServicesId,
app_id=specification.applicationId,
interview_id=specification.interviewId,
spec_id=specification.id,
compiled_route_id=function.compiledRouteId,
function_id=function.id,
completed_app_id=compiled_route.completedAppId,
)
async def get_object_type_referred_functions(object_type_id: str) -> list[str]:
referred_object_fields = await ObjectType.prisma().find_first_or_raise(
where={"id": object_type_id},
include={
"ReferredRequestAPIRoutes": True,
"ReferredResponseAPIRoutes": True,
},
)
functions_on_request = referred_object_fields.ReferredRequestAPIRoutes or []
functions_on_response = referred_object_fields.ReferredResponseAPIRoutes or []
referred_functions = []
for route in functions_on_request + functions_on_response:
referred_functions.append(route.functionName)
return referred_functions