Skip to content

Conversation

@jorschac
Copy link

Description

Implemented layered RAG query strategy for wiki page generation as mentioned in #447. Refactored wiki generation logic from api/websocket_wiki.py into modular components with improved error handling and resource management.

Files Changed:

  • Created: api/tools/rag_layers.py, api/tools/wiki_exceptions.py, api/tools/wiki_validator.py, api/tools/wiki_resources.py
  • Modified: api/websocket_wiki.py
  • Tests: tests/unit/test_wiki_generator.py

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Testing

  • Tests pass locally
  • Added new tests
  • Updated existing tests

Checklist

  • Code follows style guidelines
  • Self-reviewed my code
  • Commented complex code
  • Updated documentation
  • No breaking changes

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jorschac, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant overhaul to the wiki page generation system, focusing on improving the quality, efficiency, and robustness of the process. By adopting a layered RAG approach, modularizing the codebase, and enabling parallel generation, the system can now produce more comprehensive and contextually rich wiki content faster. The changes also enhance error handling and resource management, making the generation process more reliable and maintainable.

Highlights

  • Layered RAG Query Strategy: Implemented a sophisticated multi-layer RAG (Retrieval Augmented Generation) query strategy to enhance the quality and contextual relevance of generated wiki pages. This includes queries for core content, architecture, and broader context, followed by document deduplication and ranking.
  • Modular Refactoring of Wiki Generation Logic: The wiki generation logic has been extensively refactored from a single file (api/websocket_wiki.py) into several modular components, including rag_layers.py for core generation, wiki_exceptions.py for custom errors, wiki_resources.py for resource management, and wiki_validator.py for input validation.
  • Parallel Wiki Page Generation: Introduced parallel processing for wiki page generation using LLMService.parallel_invoke, significantly improving performance. This includes optimized concurrency settings and a retry mechanism for failed page generations.
  • Enhanced Error Handling and Resource Management: Custom exceptions (WikiGenerationError, ValidationError, RAGIndexError, PageGenerationError) have been added for more granular error handling. Resource management is improved with RAGContext and WebSocketGuard context managers to ensure proper cleanup.
  • Comprehensive Wiki Structure Validation and Conversion: A new WikiStructureValidator ensures the integrity of input wiki structures. Additionally, logic for converting enhanced wiki structures to a legacy-compatible format, while preserving hierarchical relationships, has been implemented.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-structured refactoring of the wiki page generation logic, moving it into modular components with a layered RAG query strategy. The use of dependency injection, context managers for resource management, and dedicated modules for validation and exceptions are excellent architectural choices that improve maintainability and testability. My review focuses on enhancing robustness, configurability, and code clarity. Key suggestions include removing hardcoded model parameters, improving error handling with more specific exception catches, simplifying complex resource cleanup logic, and increasing code consistency. Overall, this is a strong contribution, and the suggested changes aim to further polish the implementation.

Comment on lines +371 to +373
"model_name": "gpt-4.1",
"temperature": 0.2,
"max_tokens": 65535
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The model_name and max_tokens are hardcoded. This reduces flexibility and requires code changes to use a different model or adjust token limits. These values should be configurable, for example by adding them to the WikiPageGenerationRequest model or loading them from a configuration file. This would allow callers to specify the desired model and parameters.

Comment on lines +209 to +210
except:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a bare except is discouraged as it can suppress all errors, including system-exiting ones like SystemExit or KeyboardInterrupt, making debugging difficult. It's better to catch a specific exception, such as Exception, and log the error to provide visibility into what went wrong during the error handling process.

Suggested change
except:
pass
except Exception as send_error:
logger.warning(f"Failed to send final error to websocket: {send_error}")

prompt = f"""Generate comprehensive wiki page content for "{page['title']}" in the repository {request.repo_url}.

This page should focus on the following files:
{chr(10).join(f"- {path}" for path in page.get('filePaths', []))}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using chr(10) for a newline character is less common and less readable than the standard \n escape sequence. For better code clarity and adherence to Python conventions, it's recommended to use \n.

Suggested change
{chr(10).join(f"- {path}" for path in page.get('filePaths', []))}
{'\n'.join(f"- {path}" for path in page.get('filePaths', []))}

Comment on lines +396 to +398
if 'results' in locals() and results:
logger.warning(f"Partial failure in parallel generation. Processing {len(results)} results despite error: {str(e)}")
return results
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Checking for a local variable's existence using 'results' in locals() is fragile and not a standard practice. A more robust and readable approach is to initialize results to None at the beginning of the try block (e.g., on line 354) and then simply check if results: in the except block. This makes the code's intent clearer and avoids relying on the locals() dictionary.

Suggested change
if 'results' in locals() and results:
logger.warning(f"Partial failure in parallel generation. Processing {len(results)} results despite error: {str(e)}")
return results
if results:
logger.warning(f"Partial failure in parallel generation. Processing {len(results)} results despite error: {str(e)}")
return results

logger.info(f" - Total requested: {total_requested}")
logger.info(f" - Successfully generated: {total_successful}")
logger.info(f" - Failed: {total_failed}")
logger.info(f" - Success rate: {total_successful/total_requested*100:.1f}%")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is a potential for a ZeroDivisionError here if total_requested is 0. Although other checks in the code make this scenario unlikely, it's a good practice to guard against division by zero to make the code more robust. A similar issue exists on line 638. You could handle this with a conditional check, for example: success_rate_str = f'{(total_successful/total_requested*100):.1f}%' if total_requested > 0 else 'N/A'.

"message": f"正在重试 {total_failed} 个失败页面..."
}))

await asyncio.sleep(5)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This hardcoded sleep(5) is a "magic number". Its purpose isn't immediately clear from the code. To improve maintainability, it's best to add a comment explaining why the delay is necessary (e.g., to avoid hitting API rate limits).

Suggested change
await asyncio.sleep(5)
# Wait for a few seconds before retrying to avoid hitting API rate limits.
await asyncio.sleep(5)

Comment on lines +33 to +43
if hasattr(self.rag, 'cleanup'):
if hasattr(self.rag.cleanup, '__call__'):
# Check if it's an async method
if hasattr(self.rag.cleanup, '__code__'):
import inspect
if inspect.iscoroutinefunction(self.rag.cleanup):
await self.rag.cleanup()
else:
self.rag.cleanup()
else:
await self.rag.cleanup()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to determine if self.rag.cleanup is an awaitable coroutine is overly complex and contains a potential bug. The else block on line 42 unconditionally awaits self.rag.cleanup, which will raise a TypeError if it's a regular synchronous function that doesn't have a __code__ attribute. A simpler and more reliable approach is to use inspect.iscoroutinefunction to check if the method is async.

                if hasattr(self.rag, 'cleanup') and callable(self.rag.cleanup):
                    import inspect
                    if inspect.iscoroutinefunction(self.rag.cleanup):
                        await self.rag.cleanup()
                    else:
                        self.rag.cleanup()

Comment on lines +12 to +24
"""验证 wiki 结构的有效性"""

@staticmethod
def validate_wiki_structure(structure: Dict[str, Any]) -> None:
"""
验证 wiki 结构,失败时抛出 ValidationError

Args:
structure: Wiki 结构字典

Raises:
ValidationError: 如果结构无效
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The docstrings in this file are in Chinese, while the file-level docstring and the rest of the codebase are in English. To maintain consistency and improve readability for all contributors, it's best to use a single language throughout the project. Please consider translating these docstrings to English.

class WikiStructureValidator:
    """Validates the wiki structure.

    Ensures that the input wiki structure conforms to the required format
    before processing begins.
    """

    @staticmethod
    def validate_wiki_structure(structure: Dict[str, Any]) -> None:
        """
        Validates the wiki structure, raising a ValidationError on failure.

        Args:
            structure: The wiki structure dictionary.

        Raises:
            ValidationError: If the structure is invalid.
        """

Comment on lines +23 to +26
else:
print(f"⚠️ Warning: .env file not found at {env_path}")
print(" Please create a .env file with OPENAI_API_KEYS and OPENAI_BASE_URL")
sys.exit(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using sys.exit(1) when a .env file is not found is too aggressive for a test suite. This will halt the entire test run, preventing other tests that might not depend on environment variables from executing. It's better to print a warning and allow the specific tests that require these variables to fail gracefully. This makes the test suite more resilient and provides more complete feedback.

Suggested change
else:
print(f"⚠️ Warning: .env file not found at {env_path}")
print(" Please create a .env file with OPENAI_API_KEYS and OPENAI_BASE_URL")
sys.exit(1)
else:
print(f"⚠️ Warning: .env file not found at {env_path}")
print(" Please create a .env file with OPENAI_API_KEYS and OPENAI_BASE_URL for integration tests.")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant