Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic API key setup with core APIs #226

Merged
merged 5 commits into from
Jan 13, 2025
Merged

Basic API key setup with core APIs #226

merged 5 commits into from
Jan 13, 2025

Conversation

dhirenmathur
Copy link
Contributor

@dhirenmathur dhirenmathur commented Jan 13, 2025

Summary by CodeRabbit

  • New Features

    • Added API key management functionality
    • Enhanced conversation and parsing API endpoints
    • Introduced streaming option for message responses
  • Improvements

    • Improved error handling for API key and email operations
    • Updated input validation for code query tools
    • Refined access control for conversation interactions
    • Streamlined initialization process for agents
  • Dependency Updates

    • Updated CrewAI package version
    • Updated agentops package version
    • Removed embedchain package

Copy link

coderabbitai bot commented Jan 13, 2025

Warning

Rate limit exceeded

@dhirenmathur has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c2f43d1 and fbc2c8a.

📒 Files selected for processing (4)
  • app/modules/intelligence/provider/provider_service.py (2 hunks)
  • app/modules/intelligence/tools/change_detection/change_detection_tool.py (1 hunks)
  • app/modules/intelligence/tools/kg_based_tools/ask_knowledge_graph_queries_tool.py (1 hunks)
  • app/modules/intelligence/tools/kg_based_tools/get_nodes_from_tags_tool.py (1 hunks)

Walkthrough

This pull request introduces significant changes to the application's API and authentication infrastructure. A new FastAPI router is added with endpoints for managing conversations and parsing operations. The APIKeyService class is implemented for secure API key generation, validation, and management. Modifications to various controllers and services enhance support for streaming responses and improve error handling. Additionally, new API key management endpoints are introduced, along with updates to email validation. Overall, these changes enhance the application's functionality, security, and flexibility in API interactions.

Changes

File Change Summary
app/api/router.py Added new FastAPI router with endpoints for conversations, parsing, and message management; introduced SimpleConversationRequest model and API key validation.
app/main.py Included new potpie_api_router under /api/v2 prefix.
app/modules/auth/api_key_service.py New APIKeyService class for managing API key generation, validation, and storage.
app/modules/conversations/conversation/conversation_controller.py Updated post_message and regenerate_last_message methods to include stream parameter.
app/modules/conversations/conversation/conversation_service.py Modified check_conversation_access, store_message, and regenerate_last_message methods to include stream parameter.
app/modules/conversations/conversations_router.py Added stream parameter to post_message and regenerate_last_message methods.
app/modules/intelligence/tools/code_query_tools/get_node_neighbours_from_node_id_tool.py Introduced GetNodeNeighboursInput model for input validation.
app/modules/key_management/secret_manager.py Added API key management endpoints: create_api_key, revoke_api_key, and get_api_key.
app/modules/key_management/secrets_schema.py Added APIKeyResponse model for API key response structure.
app/modules/parsing/graph_construction/parsing_controller.py Improved error handling for user_email in parse_directory and handle_new_project methods.
app/modules/utils/email_helper.py Added validation for the to_address parameter in send_email method.
requirements.txt Updated crewai package version and removed embedchain.

Sequence Diagram

sequenceDiagram
    participant Client
    participant APIRouter
    participant APIKeyService
    participant ConversationController
    participant ConversationService

    Client->>APIRouter: POST /conversations/
    APIRouter->>APIKeyService: Validate API Key
    APIKeyService-->>APIRouter: User Details
    APIRouter->>ConversationController: Create Conversation
    ConversationController->>ConversationService: Store Conversation
    ConversationService-->>ConversationController: Conversation Created
    ConversationController-->>APIRouter: Conversation Response
    APIRouter-->>Client: Conversation Details
Loading

Possibly related PRs

  • Streaming and Agent Routing using langgraph #213: The changes in the main PR introduce new endpoints for managing conversations and parsing operations, which may relate to the streaming capabilities being developed in the WIP PR, particularly in how responses are handled and streamed back to clients.

Poem

🐰 A Rabbit's API Tale 🌐
With routers dancing, keys so bright,
Conversations stream with delight!
Parsing projects, messages clear,
CodeRabbit's magic is now here!
Security wrapped in furry might! 🔐


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🔭 Outside diff range comments (1)
requirements.txt (1)

Line range hint 1-64: Consider adding JWT packages for API key authentication

Since this PR implements API key setup, consider adding packages like:

  • python-jose for JWT token handling
  • passlib for password hashing

Apply this diff to add these security-focused packages:

sentry-sdk[fastapi]==2.19.0
posthog==3.7.4
newrelic==9.0.0
tiktoken==0.7.0
agentops==0.3.18
pydantic==2.10.3
+python-jose[cryptography]==3.3.0
+passlib[bcrypt]==1.7.4
🧹 Nitpick comments (19)
app/api/router.py (4)

91-95: Simplify the validation of message.content.

The condition checking if message.content is empty or whitespace can be made more concise and readable.

Apply this diff to simplify the condition:

-    if message.content == "" or message.content is None or message.content.isspace():
+    if not message.content or message.content.strip() == "":

23-25: Add a docstring to SimpleConversationRequest for clarity.

Providing a docstring enhances maintainability and helps other developers understand the purpose of the model.

Apply this diff:

class SimpleConversationRequest(BaseModel):
+    """
+    Request model for creating a conversation using project and agent IDs.
+    """
    project_ids: List[str]
    agent_ids: List[str]

58-59: Consider using timezone-aware timestamps in conversation titles.

Using datetime.now() without specifying a timezone may lead to inconsistencies across different environments or servers.

Apply this diff to include UTC timezone:

-    title=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+    title=datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC"),

Alternatively, use timezone-aware datetime objects.


2-2: Ensure proper imports for streaming responses.

Since you're using StreamingResponse, verify that all necessary imports are present and that the client can handle streamed responses correctly.

If not already imported, add:

+from fastapi.responses import StreamingResponse
app/modules/auth/api_key_service.py (5)

18-20: Use capitalized environment variable names for consistency.

Environment variables are conventionally uppercase. Replace isDevelopmentMode with IS_DEVELOPMENT_MODE for clarity and consistency.

Apply this diff:

-    is_dev_mode = os.getenv("isDevelopmentMode", "enabled") == "enabled"
+    is_dev_mode = os.getenv("IS_DEVELOPMENT_MODE", "enabled") == "enabled"

...

-            if os.getenv("isDevelopmentMode") != "enabled":
+            if os.getenv("IS_DEVELOPMENT_MODE") != "enabled":

...

-            if os.getenv("isDevelopmentMode") != "enabled":
+            if os.getenv("IS_DEVELOPMENT_MODE") != "enabled"

...

-            if os.getenv("isDevelopmentMode") == "enabled":
+            if os.getenv("IS_DEVELOPMENT_MODE") == "enabled":

Ensure to update any environment configuration files or deployment settings to use the new variable name.

Also applies to: 67-69, 129-131, 148-149

🧰 Tools
🪛 Ruff (0.8.2)

18-18: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)

🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed


33-36: Specify exception chaining when re-raising exceptions.

When catching exceptions and raising new ones, use raise ... from e to preserve the exception context. This helps with debugging by maintaining the original traceback.

Apply this diff:

-            except Exception as e:
-                raise HTTPException(
-                    status_code=500,
-                    detail=f"Failed to initialize Secret Manager client: {str(e)}"
-                )
+            except Exception as e:
+                raise HTTPException(
+                    status_code=500,
+                    detail=f"Failed to initialize Secret Manager client: {str(e)}"
+                ) from e

Repeat this change for the other exception handlers:

-            except Exception as e:
...
-                raise HTTPException(status_code=500, detail=f"Failed to store API key: {str(e)}")
+            except Exception as e:
+                raise HTTPException(status_code=500, detail=f"Failed to store API key: {str(e)}") from e

...

-            except Exception as e:
...
-                raise HTTPException(
-                    status_code=500,
-                    detail=f"Failed to retrieve API key: {str(e)}"
-                )
+            except Exception as e:
+                raise HTTPException(
+                    status_code=500,
+                    detail=f"Failed to retrieve API key: {str(e)}"
+                ) from e

Also applies to: 84-90, 158-162

🧰 Tools
🪛 Ruff (0.8.2)

33-36: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed


134-137: Use contextlib.suppress to simplify exception handling.

When you intend to ignore specific exceptions, contextlib.suppress provides a cleaner way to do so.

Apply this diff:

-            try:
-                client.delete_secret(request={"name": name})
-            except Exception:
-                pass  # Ignore if secret doesn't exist
+            with contextlib.suppress(Exception):
+                client.delete_secret(request={"name": name})

Don't forget to import contextlib at the top of the file:

+import contextlib
🧰 Tools
🪛 Ruff (0.8.2)

134-137: Use contextlib.suppress(Exception) instead of try-except-pass

(SIM105)

🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed


9-9: Remove unnecessary import of text if no longer used.

If you refactor the raw SQL query, the import from sqlalchemy import text may no longer be needed.

Apply this diff:

-from sqlalchemy import text
🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed


5-5: Clarify exception messages for better debugging.

When raising HTTPException, providing clear and consistent error details aids in debugging and user understanding.

Review the exception messages to ensure clarity and consistency throughout the code.

🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed

app/modules/key_management/secrets_schema.py (2)

50-51: Ensure APIKeyResponse is correctly integrated and documented.

Adding the new APIKeyResponse model is a good practice. Make sure it's properly used in the API responses and consider adding a docstring for clarity.

Apply this diff:

class APIKeyResponse(BaseModel):
+    """
+    Response model for returning an API key.
+    """
    api_key: str

49-49: Maintain consistent code style and remove trailing whitespace.

There is a blank line with trailing whitespace which can be removed to adhere to code style guidelines.

Apply this diff:

-    
app/modules/utils/email_helper.py (1)

20-20: Remove trailing whitespace.

There is trailing whitespace on this line that should be removed.

-        
+
🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed

app/modules/conversations/conversations_router.py (1)

Line range hint 83-107: Add type hints and improve documentation for streaming support.

The streaming implementation looks good, but could benefit from these improvements:

-    async def post_message(
+    async def post_message(
+        """Post a new message to the conversation.
+        
+        Args:
+            conversation_id: The ID of the conversation
+            message: The message content and metadata
+            stream: Whether to stream the response chunk by chunk
+            
+        Returns:
+            Union[StreamingResponse, dict]: A streaming response or a complete response
+        """
         conversation_id: str,
         message: MessageRequest,
         stream: bool = Query(True, description="Whether to stream the response"),
         db: Session = Depends(get_db),
         user=Depends(AuthService.check_auth),
-    ):
+    ) -> Union[StreamingResponse, dict]:
app/modules/parsing/graph_construction/parsing_controller.py (2)

36-41: Simplify dictionary access using dict.get().

The current implementation can be simplified using Python's dict.get() method.

-        if "email" not in user:
-            user_email = None
-        else:
-            user_email = user["email"]
+        user_email = user.get("email")
🧰 Tools
🪛 Ruff (0.8.2)

37-40: Use ternary operator user_email = None if "email" not in user else user["email"] instead of if-else-block

Replace if-else-block with user_email = None if "email" not in user else user["email"]

(SIM108)


37-40: Use user_email = user.get("email", None) instead of an if block

Replace with user_email = user.get("email", None)

(SIM401)

🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed


221-223: Remove redundant null check.

This check is unnecessary as user_email would already be None if not present in the user dictionary.

-        if not user_email:
-            user_email = None
-
🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed

app/modules/key_management/secret_manager.py (2)

26-46: Add logging for development mode.

Consider adding logging when running in development mode to help with debugging.

     def get_client_and_project():
         """Get Secret Manager client and project ID based on environment."""
         is_dev_mode = os.getenv("isDevelopmentMode", "enabled") == "enabled"
         if is_dev_mode:
+            logger.debug("Running in development mode, returning None for client and project")
             return None, None
🧰 Tools
🪛 Ruff (0.8.2)

27-27: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


42-45: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


220-283: Consider adding rate limiting for API key operations.

The API key management endpoints should have rate limiting to prevent abuse.

Consider implementing rate limiting using FastAPI's dependencies:

from fastapi import Depends
from fastapi.security import APIKeyHeader
from app.core.rate_limiter import RateLimiter

rate_limiter = RateLimiter(requests_per_minute=5)

@router.post("/api-keys", response_model=APIKeyResponse, dependencies=[Depends(rate_limiter)])
🧰 Tools
🪛 Ruff (0.8.2)

222-222: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


223-223: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


240-243: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


247-247: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


248-248: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


267-267: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


268-268: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


280-283: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

app/modules/conversations/conversation/conversation_service.py (2)

272-273: Document the behavior for null user_email.

Add documentation to explain why WRITE access is granted when user_email is None.

     async def check_conversation_access(
         self, conversation_id: str, user_email: str
     ) -> str:
+        """Check conversation access level for a user.
+        
+        Args:
+            conversation_id: The ID of the conversation
+            user_email: The email of the user. If None, assumes API key access
+                       and grants WRITE access.
+        """
         if not user_email:
             return ConversationAccessType.WRITE

471-477: Remove or document commented code.

Either remove the commented-out code for storing complete responses or document why it's being kept for future reference.

If this code is kept for future implementation:

-                    # # Store the complete response as a single message
-                    # self.history_manager.add_message_chunk(
-                    #     conversation_id, full_response, MessageType.AI, user_id
-                    # )
-                    # self.history_manager.flush_message_buffer(
-                    #     conversation_id, MessageType.AI, user_id
-                    # )
+                    # TODO: Future enhancement - Store complete response as a single message
+                    # Currently disabled to maintain consistency with streaming mode
+                    # self.history_manager.add_message_chunk(...)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80a5284 and 687af61.

📒 Files selected for processing (12)
  • app/api/router.py (1 hunks)
  • app/main.py (2 hunks)
  • app/modules/auth/api_key_service.py (1 hunks)
  • app/modules/conversations/conversation/conversation_controller.py (2 hunks)
  • app/modules/conversations/conversation/conversation_service.py (5 hunks)
  • app/modules/conversations/conversations_router.py (3 hunks)
  • app/modules/intelligence/tools/code_query_tools/get_node_neighbours_from_node_id_tool.py (2 hunks)
  • app/modules/key_management/secret_manager.py (3 hunks)
  • app/modules/key_management/secrets_schema.py (1 hunks)
  • app/modules/parsing/graph_construction/parsing_controller.py (2 hunks)
  • app/modules/utils/email_helper.py (1 hunks)
  • requirements.txt (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Pre-commit
app/modules/utils/email_helper.py

[warning] Trailing whitespace needs to be removed

app/modules/parsing/graph_construction/parsing_controller.py

[warning] Trailing whitespace needs to be removed

app/modules/auth/api_key_service.py

[warning] Trailing whitespace needs to be removed

app/modules/key_management/secret_manager.py

[error] 103-103: Redefinition of unused get_secret_id from line 47

🪛 Ruff (0.8.2)
app/modules/parsing/graph_construction/parsing_controller.py

37-40: Use ternary operator user_email = None if "email" not in user else user["email"] instead of if-else-block

Replace if-else-block with user_email = None if "email" not in user else user["email"]

(SIM108)


37-40: Use user_email = user.get("email", None) instead of an if block

Replace with user_email = user.get("email", None)

(SIM401)

app/modules/conversations/conversations_router.py

117-117: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


118-118: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

app/modules/conversations/conversation/conversation_controller.py

99-99: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

app/modules/conversations/conversation/conversation_service.py

542-542: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

app/modules/auth/api_key_service.py

18-18: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


33-36: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


67-67: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


89-89: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


129-129: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


134-137: Use contextlib.suppress(Exception) instead of try-except-pass

(SIM105)


148-148: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


159-162: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

app/api/router.py

29-29: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


52-52: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


53-53: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


71-71: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


72-72: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


79-79: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


80-80: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


88-88: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


89-89: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

app/modules/key_management/secret_manager.py

27-27: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


42-45: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


49-49: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


222-222: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


223-223: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


240-243: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


247-247: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


248-248: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


267-267: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


268-268: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


280-283: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (9)
app/modules/auth/api_key_service.py (1)

3-3: Use secure hashing algorithms for API key hashing.

While SHA-256 is generally secure, consider using a salt or a key derivation function like bcrypt or scrypt for hashing sensitive data like API keys.

[security]

If appropriate, modify the hash_api_key method:

-import hashlib
+import bcrypt

...

-    return hashlib.sha256(api_key.encode()).hexdigest()
+    return bcrypt.hashpw(api_key.encode(), bcrypt.gensalt()).decode()

Ensure you handle hashing and verification accordingly.

🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed

app/modules/utils/email_helper.py (1)

21-22: LGTM! Good defensive programming.

The added check for empty to_address prevents unnecessary API calls and potential errors.

🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed

app/modules/intelligence/tools/code_query_tools/get_node_neighbours_from_node_id_tool.py (2)

12-14: LGTM! Good use of Pydantic for input validation.

The GetNodeNeighboursInput model with Field descriptions improves input validation and API documentation.


124-124: LGTM! Proper schema integration.

Adding the args_schema parameter properly integrates the Pydantic model with the StructuredTool.

app/modules/conversations/conversation/conversation_controller.py (2)

84-89: LGTM! Good streaming support addition.

Adding the stream parameter with a default value of True maintains backward compatibility while enabling streaming responses.


Line range hint 92-94: LGTM! More accurate HTTP status code.

Changed status code from 401 to 403 for AccessTypeReadError, which is more semantically correct as 403 indicates forbidden access rather than unauthorized authentication.

🧰 Tools
🪛 Ruff (0.8.2)

81-81: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

app/main.py (1)

117-119: LGTM! Good API versioning practice.

Adding the new router under /api/v2 prefix is a good practice for introducing potentially breaking changes while maintaining backward compatibility.

requirements.txt (2)

Line range hint 1-64: Verify security advisories for critical packages

Let's verify there are no known vulnerabilities in the security-critical packages.

✅ Verification successful

All critical packages are using secure versions

The security audit shows that while several packages had known vulnerabilities in the past, all of them are using versions that include the necessary security patches. No action required.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for security advisories in critical packages
for pkg in "fastapi" "google-cloud-secret-manager" "firebase-admin" "openai" "requests" "aiohttp"
do
  echo "Checking $pkg..."
  gh api graphql -f query='{
    securityVulnerabilities(first: 5, ecosystem: PIP, package: "'$pkg'") {
      nodes {
        advisory {
          summary
          severity
          publishedAt
        }
        vulnerableVersionRange
        firstPatchedVersion {
          identifier
        }
      }
    }
  }'
done

Length of output: 6066


45-45: Verify compatibility with major crewai version update

The update from crewai 0.70.1 to 0.95.0 is significant. Please ensure this doesn't introduce breaking changes with other dependencies, particularly with langchain packages.

Run this script to check for potential compatibility issues:

✅ Verification successful

CrewAI 0.95.0 update appears compatible with existing codebase

The breaking changes in crewAI (type-safe output and memory-less agents) don't conflict with current usage patterns. The codebase already uses type-safe practices with Pydantic, and there's no reliance on agent memory features. LangChain integration is minimal and uses up-to-date provider-specific packages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check release notes and breaking changes
curl -s https://api.github.com/repos/joaomdmoura/crewAI/releases | jq '.[].body' | grep -i "breaking"

# Check if any files import both crewai and langchain
rg -l "from crew" | xargs rg "from langchain"

Length of output: 420


Script:

#!/bin/bash
# Check crewai usage patterns
echo "=== CrewAI Usage ==="
rg "from crew" -A 3

echo -e "\n=== LangChain Dependencies ==="
rg "langchain" requirements.txt

echo -e "\n=== GitHub Releases ==="
gh api repos/joaomdmoura/crewAI/releases --paginate | jq -r '.[] | select(.body != null) | .body' | grep -i "break" || true

Length of output: 3858

Comment on lines +28 to +29
x_api_key: Optional[str] = Header(None),
db: Session = Depends(get_db)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid calling Depends directly in default argument values.

Using Depends directly in the default values of function arguments can lead to unexpected behavior. FastAPI recommends declaring dependencies in the function parameters without default values.

Apply this diff to fix the issue:

-    db: Session = Depends(get_db)
+    db: Session = Depends(get_db),

Adjust the function signatures accordingly:

async def get_api_key_user(
    x_api_key: Optional[str] = Header(None),
-    db: Session = Depends(get_db)
+    db: Session = Depends(get_db),
) -> dict:

async def create_conversation(
    conversation: SimpleConversationRequest,
-    db: Session = Depends(get_db),
-    user=Depends(get_api_key_user),
+    db: Session = Depends(get_db),
+    user=Depends(get_api_key_user),
):

async def parse_directory(
    repo_details: ParsingRequest,
-    db: Session = Depends(get_db),
-    user=Depends(get_api_key_user),
+    db: Session = Depends(get_db),
+    user=Depends(get_api_key_user),
):

async def get_parsing_status(
    project_id: str,
-    db: Session = Depends(get_db),
-    user=Depends(get_api_key_user),
+    db: Session = Depends(get_db),
+    user=Depends(get_api_key_user),
):

async def post_message(
    conversation_id: str,
    message: MessageRequest,
-    db: Session = Depends(get_db),
-    user=Depends(get_api_key_user),
+    db: Session = Depends(get_db),
+    user=Depends(get_api_key_user),
):

Also applies to: 52-53, 71-72, 79-80, 88-89

🧰 Tools
🪛 Ruff (0.8.2)

29-29: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

Comment on lines +100 to +101
message_stream = controller.post_message(conversation_id, message, stream=False)
return StreamingResponse(message_stream, media_type="text/event-stream")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistent streaming configuration in post_message.

You're setting stream=False when calling controller.post_message, but wrapping the result in a StreamingResponse. To properly stream the response, set stream=True.

Apply this diff:

-    message_stream = controller.post_message(conversation_id, message, stream=False)
+    message_stream = controller.post_message(conversation_id, message, stream=True)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
message_stream = controller.post_message(conversation_id, message, stream=False)
return StreamingResponse(message_stream, media_type="text/event-stream")
message_stream = controller.post_message(conversation_id, message, stream=True)
return StreamingResponse(message_stream, media_type="text/event-stream")

Comment on lines +39 to +40
user = await APIKeyService.validate_api_key(x_api_key, db)
if not user:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure validate_api_key is an async function or remove await.

The call to APIKeyService.validate_api_key is awaited, implying it's an asynchronous function. Confirm that validate_api_key is defined using async def. If it's a synchronous function, remove the await keyword.

If validate_api_key is synchronous, apply this diff:

-    user = await APIKeyService.validate_api_key(x_api_key, db)
+    user = APIKeyService.validate_api_key(x_api_key, db)

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +102 to +104
user_pref = db.query(UserPreferences).filter(
text("preferences->>'api_key_hash' = :hashed_key")
).params(hashed_key=hashed_key).first()
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid raw SQL queries when possible for better security and maintainability.

Using SQLAlchemy's ORM features is safer and more maintainable than executing raw SQL strings.

Consider refactoring the query to use SQLAlchemy's filtering capabilities:

-            user_pref = db.query(UserPreferences).filter(
-                text("preferences->>'api_key_hash' = :hashed_key")
-            ).params(hashed_key=hashed_key).first()
+            from sqlalchemy.dialects.postgresql import JSONB
+            user_pref = db.query(UserPreferences).filter(
+                UserPreferences.preferences['api_key_hash'].astext == hashed_key
+            ).first()

Ensure that you have imported the necessary modules:

+from sqlalchemy.dialects.postgresql import JSONB
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user_pref = db.query(UserPreferences).filter(
text("preferences->>'api_key_hash' = :hashed_key")
).params(hashed_key=hashed_key).first()
from sqlalchemy.dialects.postgresql import JSONB
user_pref = db.query(UserPreferences).filter(
UserPreferences.preferences['api_key_hash'].astext == hashed_key
).first()
🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] Trailing whitespace needs to be removed

@@ -96,11 +96,11 @@ async def post_message(
raise HTTPException(status_code=500, detail=str(e))

async def regenerate_last_message(
self, conversation_id: str, node_ids: List[NodeContext] = []
self, conversation_id: str, node_ids: List[NodeContext] = [], stream: bool = True
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix mutable default argument.

Using an empty list [] as a default argument is a Python gotcha as it's mutable and shared between function calls.

-    async def regenerate_last_message(self, conversation_id: str, node_ids: List[NodeContext] = [], stream: bool = True)
+    async def regenerate_last_message(self, conversation_id: str, node_ids: List[NodeContext] | None = None, stream: bool = True)

Then initialize the list inside the function:

async def regenerate_last_message(...):
    if node_ids is None:
        node_ids = []
    # rest of the function
🧰 Tools
🪛 Ruff (0.8.2)

99-99: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

Comment on lines +116 to +131
stream: bool = Query(True, description="Whether to stream the response"),
db: Session = Depends(get_db),
user=Depends(AuthService.check_auth),
):
user_id = user["user_id"]
user_email = user["email"]
controller = ConversationController(db, user_id, user_email)
return StreamingResponse(
controller.regenerate_last_message(conversation_id, request.node_ids),
media_type="text/event-stream",
)
message_stream = controller.regenerate_last_message(conversation_id, request.node_ids, stream)
if stream:
return StreamingResponse(message_stream, media_type="text/event-stream")
else:
# Collect all chunks into a complete response
full_response = ""
async for chunk in message_stream:
full_response += chunk
return {"content": full_response}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove response_model for streaming responses.

The response_model=MessageResponse annotation conflicts with the streaming response type. When stream=True, the endpoint returns a StreamingResponse which doesn't match the MessageResponse model.

-    @router.post(
-        "/conversations/{conversation_id}/regenerate/", response_model=MessageResponse
-    )
+    @router.post("/conversations/{conversation_id}/regenerate/")
     async def regenerate_last_message(
         conversation_id: str,
         request: RegenerateRequest,
         stream: bool = Query(True, description="Whether to stream the response"),
         db: Session = Depends(get_db),
         user=Depends(AuthService.check_auth),
     ):

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.8.2)

117-117: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


118-118: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

Comment on lines +47 to +57
@staticmethod
def get_secret_id(provider: Literal["openai", "anthropic"], customer_id: str):
if os.getenv("isDevelopmentMode") == "enabled":
return None
if provider == "openai":
secret_id = f"openai-api-key-{customer_id}"
elif provider == "anthropic":
secret_id = f"anthropic-api-key-{customer_id}"
else:
client = None
project_id = None
return client, project_id
raise HTTPException(status_code=400, detail="Invalid provider")
return secret_id
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate method definition.

The get_secret_id method is defined twice in this file. Remove this duplicate definition.

The method is already defined later in the file at line 103. Delete this duplicate definition to fix the pipeline failure.

🧰 Tools
🪛 Ruff (0.8.2)

49-49: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)

@@ -520,7 +539,7 @@ async def _update_conversation_title(self, conversation_id: str, new_title: str)
self.sql_db.commit()

async def regenerate_last_message(
self, conversation_id: str, user_id: str, node_ids: List[NodeContext] = []
self, conversation_id: str, user_id: str, node_ids: List[NodeContext] = [], stream: bool = True
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix mutable default argument.

Using an empty list as a default argument is dangerous as it's mutable and shared between calls.

-    async def regenerate_last_message(
-        self, conversation_id: str, user_id: str, node_ids: List[NodeContext] = [], stream: bool = True
+    async def regenerate_last_message(
+        self, conversation_id: str, user_id: str, node_ids: Optional[List[NodeContext]] = None, stream: bool = True
     ) -> AsyncGenerator[str, None]:
+        if node_ids is None:
+            node_ids = []
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self, conversation_id: str, user_id: str, node_ids: List[NodeContext] = [], stream: bool = True
async def regenerate_last_message(
self, conversation_id: str, user_id: str, node_ids: Optional[List[NodeContext]] = None, stream: bool = True
) -> AsyncGenerator[str, None]:
if node_ids is None:
node_ids = []
🧰 Tools
🪛 Ruff (0.8.2)

542-542: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
app/modules/key_management/secret_manager.py (3)

41-45: Improve exception handling with error chaining.

Use error chaining to preserve the original exception context.

         except Exception as e:
             raise HTTPException(
                 status_code=500,
                 detail=f"Failed to initialize Secret Manager client: {str(e)}"
-            )
+            ) from e
🧰 Tools
🪛 Ruff (0.8.2)

42-45: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


47-57: Enhance type safety and naming conventions.

  1. Add return type hint for better type safety
  2. Use uppercase for environment variable name (same issue as above)
     @staticmethod
-    def get_secret_id(provider: Literal["openai", "anthropic"], customer_id: str):
+    def get_secret_id(provider: Literal["openai", "anthropic"], customer_id: str) -> str | None:
-        if os.getenv("isDevelopmentMode") == "enabled":
+        if os.getenv("IS_DEVELOPMENT_MODE") == "enabled":
             return None
🧰 Tools
🪛 Ruff (0.8.2)

49-49: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


209-212: Improve FastAPI dependency injection pattern.

Follow FastAPI's recommended pattern for dependency injection by moving dependencies to function parameters.

-    async def create_api_key(
-        user=Depends(AuthService.check_auth),
-        db: Session = Depends(get_db),
-    ):
+    async def create_api_key(
+        user = AuthService.check_auth,
+        db: Session = get_db,
+    ):

Apply the same pattern to revoke_api_key and get_api_key methods.

Also applies to: 234-237, 254-257

🧰 Tools
🪛 Ruff (0.8.2)

210-210: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


211-211: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 687af61 and 79ffc31.

📒 Files selected for processing (1)
  • app/modules/key_management/secret_manager.py (3 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
app/modules/key_management/secret_manager.py

27-27: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


42-45: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


49-49: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)


210-210: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


211-211: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


228-231: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


235-235: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


236-236: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


255-255: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


256-256: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


268-271: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🪛 GitHub Actions: Pre-commit
app/modules/key_management/secret_manager.py

[warning] 1-263: Code formatting issues detected - needs to be formatted using Black

🔇 Additional comments (1)
app/modules/key_management/secret_manager.py (1)

Line range hint 1-271: Format code using Black.

The pipeline failure indicates that the code needs to be formatted using Black.

Run the following command to format the code:

✅ Verification successful

Fix formatting issues using Black to address trailing whitespace and blank lines

The code requires formatting fixes:

  • Remove trailing whitespace on line 245
  • Remove extra blank line around line 21
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if the code can be formatted using Black

# Check if Black would make any changes
black --check app/modules/key_management/secret_manager.py || echo "Code needs formatting"

Length of output: 209


Script:

#!/bin/bash
# Check for basic formatting issues that Black would typically fix

# Check for lines longer than 88 characters (Black's default)
echo "Lines longer than 88 characters:"
awk 'length > 88' app/modules/key_management/secret_manager.py

# Check for inconsistent indentation (not multiples of 4)
echo -e "\nLines with indentation not divisible by 4:"
awk '/^[[:space:]]/ { if (length(substr($0, 1, match($0, /[^[:space:]]/)-1)) % 4 != 0) print NR ":" $0 }' app/modules/key_management/secret_manager.py

# Check for trailing whitespace
echo -e "\nLines with trailing whitespace:"
grep -n '[[:space:]]$' app/modules/key_management/secret_manager.py

# Check for multiple consecutive blank lines
echo -e "\nMultiple consecutive blank lines:"
awk '/^$/ { blank++ } !/^$/ { blank=0 } blank>1 { print NR-1 }' app/modules/key_management/secret_manager.py

Length of output: 752

🧰 Tools
🪛 Ruff (0.8.2)

206-206: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


210-210: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


211-211: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


228-231: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


235-235: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


236-236: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


255-255: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


256-256: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


268-271: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

Comment on lines +268 to +271
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve API key: {str(e)}"
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling in get_api_key.

  1. Use error chaining
  2. Avoid exposing internal error details
         except Exception as e:
             raise HTTPException(
                 status_code=500,
-                detail=f"Failed to retrieve API key: {str(e)}"
+                detail="Failed to retrieve API key. Please try again later."
-            )
+            ) from e
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve API key: {str(e)}"
)
raise HTTPException(
status_code=500,
detail="Failed to retrieve API key. Please try again later."
) from e
🧰 Tools
🪛 Ruff (0.8.2)

268-271: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

@@ -21,13 +23,38 @@
class SecretManager:
@staticmethod
def get_client_and_project():
if os.getenv("isDevelopmentMode") == "disabled":
"""Get Secret Manager client and project ID based on environment."""
is_dev_mode = os.getenv("isDevelopmentMode", "enabled") == "enabled"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use uppercase for environment variable names.

Follow Python's convention of using uppercase for environment variable names.

-        is_dev_mode = os.getenv("isDevelopmentMode", "enabled") == "enabled"
+        is_dev_mode = os.getenv("IS_DEVELOPMENT_MODE", "enabled") == "enabled"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
is_dev_mode = os.getenv("isDevelopmentMode", "enabled") == "enabled"
is_dev_mode = os.getenv("IS_DEVELOPMENT_MODE", "enabled") == "enabled"
🧰 Tools
🪛 Ruff (0.8.2)

27-27: Use capitalized environment variable ISDEVELOPMENTMODE instead of isDevelopmentMode

(SIM112)

Comment on lines +208 to +231
@router.post("/api-keys", response_model=APIKeyResponse)
async def create_api_key(
user=Depends(AuthService.check_auth),
db: Session = Depends(get_db),
):
"""Create a new API key for the authenticated user."""
try:
api_key = await APIKeyService.create_api_key(user["user_id"], db)
PostHogClient().send_event(
user["user_id"],
"api_key_creation",
{"success": True}
)
return {"api_key": api_key}
except Exception as e:
PostHogClient().send_event(
user["user_id"],
"api_key_creation",
{"success": False, "error": str(e)}
)
raise HTTPException(
status_code=500,
detail=f"Failed to create API key: {str(e)}"
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance security and error handling in API key creation.

  1. Avoid exposing internal error details in the response
  2. Add rate limiting to prevent abuse
  3. Improve exception handling with error chaining
     @router.post("/api-keys", response_model=APIKeyResponse)
+    @limiter.limit("3/hour")  # Add rate limiting
     async def create_api_key(
         user=Depends(AuthService.check_auth),
         db: Session = Depends(get_db),
     ):
         """Create a new API key for the authenticated user."""
         try:
             api_key = await APIKeyService.create_api_key(user["user_id"], db)
             PostHogClient().send_event(
                 user["user_id"],
                 "api_key_creation",
                 {"success": True}
             )
             return {"api_key": api_key}
         except Exception as e:
             PostHogClient().send_event(
                 user["user_id"],
                 "api_key_creation",
                 {"success": False, "error": str(e)}
             )
             raise HTTPException(
                 status_code=500,
-                detail=f"Failed to create API key: {str(e)}"
+                detail="Failed to create API key. Please try again later."
-            )
+            ) from e

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.8.2)

210-210: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


211-211: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


228-231: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
app/modules/intelligence/agents/agents/blast_radius_agent.py (1)

40-44: LGTM! Good architectural improvement.

Moving tool management to the agent level improves encapsulation and aligns with the single responsibility principle. The selected tools are appropriate for blast radius analysis.

app/modules/intelligence/agents/agents/unit_test_agent.py (1)

38-41: LGTM! Consider adding docstring analysis tool.

The tool configuration is well-structured and aligns with the architectural changes. Consider adding a docstring analysis tool to enhance test generation based on function documentation.

app/modules/conversations/conversation/conversation_service.py (1)

476-482: Remove or implement commented code.

The commented code for storing complete responses should either be implemented if needed or removed if no longer required. Keeping commented code can lead to confusion and maintenance issues.

app/modules/intelligence/tools/kg_based_tools/get_code_from_probable_node_name_tool.py (1)

5-5: Consider documenting the migration strategy

This file completes the pattern of migrating from langchain.tools to langchain_core.tools across the entire kg_based_tools module. Consider:

  1. Adding a comment explaining the rationale for this migration
  2. Documenting any version requirements or compatibility notes

Also applies to: 210-210

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79ffc31 and c2f43d1.

📒 Files selected for processing (7)
  • app/modules/conversations/conversation/conversation_service.py (6 hunks)
  • app/modules/intelligence/agents/agents/blast_radius_agent.py (1 hunks)
  • app/modules/intelligence/agents/agents/unit_test_agent.py (1 hunks)
  • app/modules/intelligence/tools/kg_based_tools/get_code_from_multiple_node_ids_tool.py (2 hunks)
  • app/modules/intelligence/tools/kg_based_tools/get_code_from_node_id_tool.py (2 hunks)
  • app/modules/intelligence/tools/kg_based_tools/get_code_from_probable_node_name_tool.py (2 hunks)
  • requirements.txt (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • requirements.txt
🧰 Additional context used
🪛 Ruff (0.8.2)
app/modules/conversations/conversation/conversation_service.py

547-547: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🪛 GitHub Actions: Pre-commit
app/modules/conversations/conversation/conversation_service.py

[warning] 172-172: Line too long (135/100 characters)


[warning] 178-178: Line too long (131/100 characters)

🔇 Additional comments (7)
app/modules/intelligence/agents/agents/unit_test_agent.py (2)

22-27: LGTM! Good initialization order and environment variable handling.

The initialization sequence is correct, with environment variables and LLM being set up before tool initialization. The default value for MAX_ITER provides a safety net.


Line range hint 156-171: LGTM! Good input validation and error handling.

The function properly validates inputs and provides a clear, actionable error message. Using a large LLM for test generation is appropriate given the complexity of the task.

app/modules/conversations/conversation/conversation_service.py (2)

580-586: Remove or implement commented code.

See previous comment about removing or implementing commented code for storing complete responses.


547-547: ⚠️ Potential issue

Fix mutable default argument.

Using an empty list as a default argument is dangerous as it's mutable and shared between function calls.

-    async def regenerate_last_message(
-        self, conversation_id: str, user_id: str, node_ids: List[NodeContext] = [], stream: bool = True
+    async def regenerate_last_message(
+        self, conversation_id: str, user_id: str, node_ids: Optional[List[NodeContext]] = None, stream: bool = True
     ) -> AsyncGenerator[str, None]:
+        if node_ids is None:
+            node_ids = []

Likely invalid or redundant comment.

🧰 Tools
🪛 Ruff (0.8.2)

547-547: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

app/modules/intelligence/tools/kg_based_tools/get_code_from_node_id_tool.py (2)

138-138: Return type annotation needs clarification

The function is annotated to return Tool but returns a StructuredTool. While this might work if there's an inheritance relationship, it would be clearer to either:

  1. Use StructuredTool as the return type annotation, or
  2. Document why Tool is the appropriate type annotation
✅ Verification successful

Return type annotation is correct

The Tool return type annotation is appropriate as StructuredTool is a subclass of Tool from the langchain_core.tools module. This pattern is consistently used across the codebase, following the principle of programming to interfaces rather than implementations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the inheritance relationship between Tool and StructuredTool
ast-grep --pattern 'class StructuredTool($$$)'

Length of output: 48


Script:

#!/bin/bash
# Check imports and full implementation
cat app/modules/intelligence/tools/kg_based_tools/get_code_from_node_id_tool.py

# Search for StructuredTool in imports or usage across codebase
rg "StructuredTool" -A 2 -B 2

Length of output: 19492


5-5: Verify langchain_core compatibility

The import has been updated from langchain.tools to langchain_core.tools. While this change aligns with the broader refactoring, ensure that the version of langchain_core being used is compatible with your project's requirements.

✅ Verification successful

Import change is correct and compatible

The migration to langchain_core.tools is properly implemented with explicit version constraints (langchain-core==0.2.43). The code correctly uses StructuredTool.from_function() while maintaining type compatibility with the Tool return type annotation through inheritance.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the installed version of langchain_core
pip show langchain-core | grep Version

# Check for any compatibility issues in requirements.txt or setup.py
rg -l "langchain" | grep -E "requirements.*\.txt|setup\.py"

Length of output: 211


Script:

#!/bin/bash
# Check langchain requirements
cat requirements.txt | grep -i "langchain"

# Check Tool/StructuredTool usage patterns
ast-grep --pattern 'return StructuredTool($$$)'
ast-grep --pattern 'return Tool($$$)'

Length of output: 2166


Script:

#!/bin/bash
# Check the implementation in the file under review
cat app/modules/intelligence/tools/kg_based_tools/get_code_from_node_id_tool.py

Length of output: 5898

app/modules/intelligence/tools/kg_based_tools/get_code_from_multiple_node_ids_tool.py (1)

5-5: Maintain consistency with Tool usage

The changes mirror those in get_code_from_node_id_tool.py. The same considerations about langchain_core compatibility and Tool vs StructuredTool type annotations apply here.

Also applies to: 159-159

Comment on lines +171 to +173
logger.info(
f"Streaming AI response for conversation {state['conversation_id']} for user {state['user_id']} using agent {agent_id}"
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix line length issues in logging statements.

The logging statements exceed the maximum line length of 100 characters. Consider breaking them into multiple lines for better readability.

-            logger.info(f"Streaming AI response for conversation {state['conversation_id']} for user {state['user_id']} using agent {agent_id}")
+            logger.info(
+                f"Streaming AI response for conversation {state['conversation_id']} "
+                f"for user {state['user_id']} using agent {agent_id}"
+            )
-            logger.info(f"Streaming AI response for conversation {state['conversation_id']} for user {state['user_id']} using agent {agent_id}")
+            logger.info(
+                f"Streaming AI response for conversation {state['conversation_id']} "
+                f"for user {state['user_id']} using agent {agent_id}"
+            )

Also applies to: 177-179

🧰 Tools
🪛 GitHub Actions: Pre-commit

[warning] 172-172: Line too long (135/100 characters)

@@ -2,7 +2,7 @@
import logging
from typing import Any, Dict, List

from langchain.tools import StructuredTool
from langchain_core.tools import StructuredTool, Tool
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Migration to langchain_core.tools should be consistent across all tool files

The following files still use the old langchain.tools import and should be updated to use langchain_core.tools for consistency:

  • app/modules/intelligence/tools/kg_based_tools/get_nodes_from_tags_tool.py
  • app/modules/intelligence/tools/kg_based_tools/ask_knowledge_graph_queries_tool.py
  • app/modules/intelligence/tools/change_detection/change_detection_tool.py
🔗 Analysis chain

Verify broader impact of langchain_core migration

Since this change affects multiple files, let's verify the broader impact:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other potential langchain tool usages that might need updating
rg -l "from langchain.tools import" --type python

# Check for direct Tool or StructuredTool usage
ast-grep --pattern 'from langchain.tools import Tool'
ast-grep --pattern 'from langchain.tools import StructuredTool'

Length of output: 574


Script:

#!/bin/bash
# Check for all langchain imports in the affected tools
rg "^from langchain" app/modules/intelligence/tools/kg_based_tools/get_nodes_from_tags_tool.py app/modules/intelligence/tools/kg_based_tools/ask_knowledge_graph_queries_tool.py app/modules/intelligence/tools/change_detection/change_detection_tool.py

# Check if langchain_core is already being used anywhere
rg "^from langchain_core" app/modules/intelligence/tools/kg_based_tools/get_nodes_from_tags_tool.py app/modules/intelligence/tools/kg_based_tools/ask_knowledge_graph_queries_tool.py app/modules/intelligence/tools/change_detection/change_detection_tool.py

Length of output: 872

@dhirenmathur dhirenmathur merged commit 050580e into main Jan 13, 2025
3 of 4 checks passed
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