From 3bad45f6a449c572b3487b7169e3678a9384f556 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:07:03 +0000 Subject: [PATCH 1/2] fix(CODEWIKI-006-2): 2 review findings in generate_sub_module_documentations.py --- .../generate_sub_module_documentations.py | 80 +++++-------------- 1 file changed, 21 insertions(+), 59 deletions(-) diff --git a/codewiki/src/be/agent_tools/generate_sub_module_documentations.py b/codewiki/src/be/agent_tools/generate_sub_module_documentations.py index f0c7a938..4f59ed84 100644 --- a/codewiki/src/be/agent_tools/generate_sub_module_documentations.py +++ b/codewiki/src/be/agent_tools/generate_sub_module_documentations.py @@ -7,7 +7,7 @@ from codewiki.src.be.llm_services import create_fallback_models from codewiki.src.be.prompt_template import SYSTEM_PROMPT, LEAF_SYSTEM_PROMPT, format_user_prompt, format_system_prompt, format_leaf_system_prompt from codewiki.src.be.utils import is_complex_module, count_tokens -from codewiki.src.be.cluster_modules import format_potential_core_components +from codewiki.src.be.cluster_modules import format_potential_core_components, normalize_component_ids_by_lookup import logging logger = logging.getLogger(__name__) @@ -41,49 +41,10 @@ async def generate_sub_module_documentation( _, _, id_to_fqdn, _ = format_potential_core_components(all_component_ids, deps.components) normalized_specs = {} - total_normalized = 0 - total_failed = 0 - for sub_module_name, component_ids in sub_module_specs.items(): - normalized_ids = [] - for comp_id in component_ids: - # Try exact FQDN match first (component_ids might already be FQDNs) - if comp_id in deps.components: - normalized_ids.append(comp_id) - # Try converting integer ID to FQDN (ID-based system) - else: - try: - # LLM should return integer IDs - idx = int(comp_id) - if idx in id_to_fqdn: - fqdn = id_to_fqdn[idx] - normalized_ids.append(fqdn) - total_normalized += 1 - logger.debug(f" ✅ Normalized ID {idx} → '{fqdn}'") - else: - logger.warning( - f" ⚠️ Failed to normalize ID {idx} in sub-module '{sub_module_name}'\n" - f" ├─ ID out of range (valid: 0-{len(id_to_fqdn)-1})\n" - f" └─ LLM returned invalid integer ID" - ) - total_failed += 1 - except (ValueError, TypeError): - # comp_id is not an integer - likely a class name (LLM ignored instructions) - similar_fqdns = [fqdn for fqdn in deps.components.keys() if str(comp_id).lower() in fqdn.lower()][:5] - logger.warning( - f" ⚠️ Failed to normalize '{comp_id}' in sub-module '{sub_module_name}'\n" - f" ├─ Not an integer ID (type: {type(comp_id).__name__})\n" - f" ├─ LLM returned class name instead of integer ID\n" - f" └─ FQDNs containing '{comp_id}': {similar_fqdns if similar_fqdns else 'None found'}" - ) - total_failed += 1 - - normalized_specs[sub_module_name] = normalized_ids - - if total_normalized > 0: - logger.info(f" ✅ Normalized {total_normalized} integer IDs to FQDNs") - if total_failed > 0: - logger.warning(f" ⚠️ Failed to normalize {total_failed} component IDs (LLM ignored instructions)") + normalized_specs[sub_module_name] = normalize_component_ids_by_lookup( + component_ids, deps.components, id_to_fqdn + ) # Replace original specs with normalized specs sub_module_specs = normalized_specs @@ -144,21 +105,22 @@ async def generate_sub_module_documentation( # log the current module tree # print(f"Current module tree: {json.dumps(deps.module_tree, indent=4)}") - # FLAMINGO_PATCH: Added usage_limits to prevent "request_limit of 50" exceeded errors - result = await sub_agent.run( - format_user_prompt( - module_name=deps.current_module_name, - core_component_ids=core_component_ids, - components=ctx.deps.components, - module_tree=ctx.deps.module_tree, - ), - deps=ctx.deps, - usage_limits=UsageLimits(request_limit=1000), - ) - - # remove the sub-module name from the path to current module and the module tree - deps.path_to_current_module.pop() - deps.current_depth -= 1 + try: + # FLAMINGO_PATCH: Added usage_limits to prevent "request_limit of 50" exceeded errors + result = await sub_agent.run( + format_user_prompt( + module_name=deps.current_module_name, + core_component_ids=core_component_ids, + components=ctx.deps.components, + module_tree=ctx.deps.module_tree, + ), + deps=ctx.deps, + usage_limits=UsageLimits(request_limit=1000), + ) + finally: + # remove the sub-module name from the path to current module and the module tree + deps.path_to_current_module.pop() + deps.current_depth -= 1 # restore the previous module name deps.current_module_name = previous_module_name @@ -196,4 +158,4 @@ async def generate_sub_module_documentation( The component identifiers must match exactly what appears in .""", takes_ctx=True -) \ No newline at end of file +) From 62a2d67f1b4972d8f61e2f7164aa65e6f4b1a096 Mon Sep 17 00:00:00 2001 From: Michael Assraf Date: Mon, 7 Sep 2026 22:59:58 -0400 Subject: [PATCH 2/2] fix: dedupe ID normalization via a shared list primitive The branch called normalize_component_ids_by_lookup(component_ids, deps.components, id_to_fqdn) - three positional arguments against a two-argument function that takes a module tree and returns a dict. That is a TypeError on every sub-module generation, which is the recursive path the doc pipeline reaches on any repo deep enough to split a module. It also dropped a behaviour: the deleted code accepted a component id that was already an FQDN present in deps.components, since the LLM sometimes echoes one straight out of the prompt. normalize_component_ids_by_lookup has no such branch. Introduce normalize_component_id_list(component_ids, id_to_fqdn, components=None, context='') -> (fqdns, normalized, failed) as the single implementation, and have both callers use it: - normalize_component_ids_by_lookup keeps its signature and dict return, and now delegates per module (verified identical output to main); - generate_sub_module_documentations passes components=deps.components, so the exact-FQDN path is preserved. Co-Authored-By: Claude Opus 5 --- .../generate_sub_module_documentations.py | 19 +++- codewiki/src/be/cluster_modules.py | 103 ++++++++++++------ 2 files changed, 85 insertions(+), 37 deletions(-) diff --git a/codewiki/src/be/agent_tools/generate_sub_module_documentations.py b/codewiki/src/be/agent_tools/generate_sub_module_documentations.py index 4f59ed84..ff717269 100644 --- a/codewiki/src/be/agent_tools/generate_sub_module_documentations.py +++ b/codewiki/src/be/agent_tools/generate_sub_module_documentations.py @@ -7,7 +7,7 @@ from codewiki.src.be.llm_services import create_fallback_models from codewiki.src.be.prompt_template import SYSTEM_PROMPT, LEAF_SYSTEM_PROMPT, format_user_prompt, format_system_prompt, format_leaf_system_prompt from codewiki.src.be.utils import is_complex_module, count_tokens -from codewiki.src.be.cluster_modules import format_potential_core_components, normalize_component_ids_by_lookup +from codewiki.src.be.cluster_modules import format_potential_core_components, normalize_component_id_list import logging logger = logging.getLogger(__name__) @@ -41,10 +41,23 @@ async def generate_sub_module_documentation( _, _, id_to_fqdn, _ = format_potential_core_components(all_component_ids, deps.components) normalized_specs = {} + total_normalized = 0 + total_failed = 0 for sub_module_name, component_ids in sub_module_specs.items(): - normalized_specs[sub_module_name] = normalize_component_ids_by_lookup( - component_ids, deps.components, id_to_fqdn + resolved, normalized, failed = normalize_component_id_list( + component_ids, + id_to_fqdn, + components=deps.components, + context=f"sub-module '{sub_module_name}'", ) + normalized_specs[sub_module_name] = resolved + total_normalized += normalized + total_failed += failed + + if total_normalized > 0: + logger.info(f" \u2705 Normalized {total_normalized} integer IDs to FQDNs") + if total_failed > 0: + logger.warning(f" \u26a0\ufe0f Failed to normalize {total_failed} component IDs") # Replace original specs with normalized specs sub_module_specs = normalized_specs diff --git a/codewiki/src/be/cluster_modules.py b/codewiki/src/be/cluster_modules.py index e7b1fa78..0034f32d 100644 --- a/codewiki/src/be/cluster_modules.py +++ b/codewiki/src/be/cluster_modules.py @@ -209,6 +209,63 @@ def format_potential_core_components( return potential_core_components, potential_core_components_with_code, id_to_fqdn, id_descriptions +def normalize_component_id_list( + component_ids: List[Any], + id_to_fqdn: Dict[int, str], + components: Optional[Dict[str, Any]] = None, + context: str = "", +) -> tuple[List[str], int, int]: + """Resolve one list of LLM-returned component ids to FQDNs. + + The LLM is asked for integer ids, but may return an FQDN it copied verbatim + out of the prompt. When ``components`` is supplied, such an id is accepted + as-is; otherwise only integer ids in ``id_to_fqdn`` resolve. + + Args: + component_ids: Raw ids from the LLM response. + id_to_fqdn: Integer id -> FQDN mapping for this prompt. + components: Optional component registry, enabling the exact-FQDN path. + context: Label used in warnings (module or sub-module name). + + Returns: + (resolved FQDNs, number normalized, number that could not be resolved) + """ + max_id = len(id_to_fqdn) - 1 + resolved: List[str] = [] + normalized = 0 + failed = 0 + + for comp_id in component_ids: + # The LLM sometimes echoes a full FQDN straight out of the prompt. + if components is not None and comp_id in components: + resolved.append(comp_id) + continue + try: + idx = int(comp_id) + except (ValueError, TypeError) as e: + logger.warning( + f" \u274c Non-integer component ID in '{context}'\n" + f" \u251c\u2500 Received: {comp_id} (type: {type(comp_id).__name__})\n" + f" \u251c\u2500 Error: {e}\n" + f" \u2514\u2500 LLM must return integer IDs only" + ) + failed += 1 + continue + if idx in id_to_fqdn: + resolved.append(id_to_fqdn[idx]) + normalized += 1 + logger.debug(f" \u2705 ID {idx} \u2192 {id_to_fqdn[idx]}") + else: + logger.warning( + f" \u274c Invalid ID {idx} in '{context}'\n" + f" \u251c\u2500 Valid range: 0-{max_id}\n" + f" \u2514\u2500 LLM returned out-of-range ID" + ) + failed += 1 + + return resolved, normalized, failed + + def normalize_component_ids_by_lookup( module_tree: Dict, id_to_fqdn: Dict[int, str] @@ -224,46 +281,24 @@ def normalize_component_ids_by_lookup( Returns: Module tree with IDs replaced by FQDNs """ - logger.info("🔄 Normalizing component IDs via direct lookup") + logger.info("\U0001f504 Normalizing component IDs via direct lookup") total_normalized = 0 total_failed = 0 - max_id = len(id_to_fqdn) - 1 for module_name, module_data in module_tree.items(): - component_ids = module_data.get('components', []) - normalized_components = [] - - for comp_id in component_ids: - # Convert to int and validate - try: - idx = int(comp_id) - if idx in id_to_fqdn: - fqdn = id_to_fqdn[idx] - normalized_components.append(fqdn) - total_normalized += 1 - logger.debug(f" ✅ ID {idx} → {fqdn}") - else: - logger.warning( - f" ❌ Invalid ID {idx} in module '{module_name}'\n" - f" ├─ Valid range: 0-{max_id}\n" - f" └─ LLM returned out-of-range ID" - ) - total_failed += 1 - except (ValueError, TypeError) as e: - logger.warning( - f" ❌ Non-integer ID in module '{module_name}'\n" - f" ├─ Received: {comp_id} (type: {type(comp_id).__name__})\n" - f" ├─ Error: {e}\n" - f" └─ LLM must return integer IDs only" - ) - total_failed += 1 - - module_data['components'] = normalized_components - - logger.info(f" ✅ Normalized {total_normalized} component IDs") + resolved, normalized, failed = normalize_component_id_list( + module_data.get('components', []), + id_to_fqdn, + context=f"module '{module_name}'", + ) + module_data['components'] = resolved + total_normalized += normalized + total_failed += failed + + logger.info(f" \u2705 Normalized {total_normalized} component IDs") if total_failed > 0: - logger.warning(f" ⚠️ Failed to normalize {total_failed} IDs") + logger.warning(f" \u26a0\ufe0f Failed to normalize {total_failed} IDs") return module_tree