| """ |
| Prompt Generator Module |
| |
| Generates structured prompts for LLM-based code explanation organized by node level. |
| """ |
| import re |
| import ast |
| import copy |
| import networkx as nx |
| from structlog import get_logger |
| from modal_client import ModalClient |
|
|
|
|
| logger = get_logger(__name__) |
|
|
|
|
|
|
| def generate_explaination_by_level(graph: nx.DiGraph, levels: dict) -> dict[int, dict]: |
| """ |
| Generate LLM prompts organized by node level. |
| |
| Creates prompts for each node that include: |
| - File path |
| - Used modules (name + content from graph successors) |
| - Node content (unparsed AST) |
| |
| Nodes without a namespace are skipped as they typically represent |
| external or incomplete references. |
| |
| Args: |
| graph: NetworkX directed graph with code nodes |
| levels: Dictionary mapping nodes to their levels |
| |
| Returns: |
| Dictionary mapping level → {node: prompt_string} |
| """ |
|
|
|
|
| |
| prompts_by_level = {} |
| |
| for level in range(max(levels.keys()) + 1): |
| if level not in levels: |
| continue |
| |
| batch = {} |
| |
| for node in levels[level]: |
| if node.namespace is None or node.get_short_name() in ["lambda" ] or node.ast_node is None: |
| continue |
| |
| if len(ast.unparse(node.ast_node))<1000: |
| |
| continue |
| prompt = prompt = """You are a Python code analysis expert. |
| |
| **CRITICAL RULES:** |
| 1. ONLY use information directly visible in the "TARGET CODE" section |
| 2. For methods marked as "[SUMMARIZED]", reference them by their actual name shown |
| 3. If a method body is replaced with a summary, DO NOT invent details about its implementation |
| 4. State "implementation details not shown" for summarized methods |
| |
| Your explanation must be brief and cover: |
| - Purpose: What this code does (1-2 sentences) |
| - Inputs: Parameters (only those visible) |
| - Outputs: Return values (only those visible) |
| - Exceptions: Only exceptions explicitly raised in the visible code (1 sentence) |
| |
| """ |
| node_copy = copy.deepcopy(node) |
| |
| |
| used_modules = [] |
| summarized_methods = [] |
| for used_node in graph.successors(node): |
| if used_node.namespace is None or used_node.get_short_name() in ["lambda" ] or used_node.ast_node is None: |
| continue |
| |
| label = graph.get_edge_data(node, used_node).get("label") |
| |
| |
| if used_node.ast_node is None: |
| continue |
| elif label == 'contains': |
| if used_node.ast_node in node.ast_node.body and\ |
| hasattr(used_node,"explination"): |
| if isinstance(used_node.ast_node, ast.FunctionDef): |
| |
| signature = f"def {used_node.ast_node.name}({ast.unparse(used_node.ast_node.args)})" |
| if used_node.ast_node.returns: |
| signature += f" -> {ast.unparse(used_node.ast_node.returns)}" |
| |
| marker_text = f"""[SUMMARIZED METHOD] |
| Method: {used_node.name} |
| Signature: {signature} |
| Summary: {used_node.explination} |
| Note: Full implementation replaced for brevity""" |
| |
| elif isinstance(used_node.ast_node, ast.ClassDef): |
| marker_text = f"""[SUMMARIZED CLASS] |
| Class: {used_node.name} |
| Summary: {used_node.explination} |
| Note: Full implementation replaced for brevity""" |
| |
| else: |
| marker_text = f"""[SUMMARIZED] |
| Name: {used_node.name} |
| Summary: {used_node.explination}""" |
| new_child = ast.Expr(value=ast.Constant(value=marker_text)) |
| for i, child in enumerate(node.ast_node.body): |
| if child == used_node.ast_node: |
| node_copy.ast_node.body[i] = new_child |
| summarized_methods.append(used_node.name) |
| break |
| pass |
| elif hasattr(used_node,"explination") is False: |
| pass |
|
|
| |
| |
| elif label == 'use': |
| |
| used_modules.append(used_node) |
| |
| |
| prompt += f"**Target File Path:** {node.filename}\n\n" |
| logger.info(f"used modules numers {len(used_modules)}") |
| if used_modules: |
| if len(used_modules) > 20: |
| pass |
| prompt += "**External Dependencies Used:**\n" |
| for used_node in used_modules: |
| if hasattr(used_node, "explination"): |
| prompt += f"""- **{used_node.name}** [EXPLAINED] |
| - File: {used_node.filename} |
| - Explanation: {used_node.explination}""" |
| else: |
| prompt += f"""- **{used_node.name}** |
| - File: {used_node.filename} |
| - Python Code: {ast.unparse(used_node.ast_node)}""" |
| |
|
|
| |
|
|
|
|
| |
| if summarized_methods: |
| prompt += f"**Note:** The following methods are summarized in the code below: {', '.join(summarized_methods)}\n\n" |
|
|
|
|
| prompt += f"""**TARGET CODE:** |
| ```python |
| {ast.unparse(node_copy.ast_node)} |
| ``` |
| |
| Explain the TARGET CODE above and Brief and precise |
| """ |
| |
| batch[node] = prompt |
| |
| if batch: |
| results = ModalClient.infer_llm(batch.values()) |
| for index, node in enumerate(batch.keys()): |
| node.explination = results[index] |
| return prompts_by_level |
|
|