Skip to content
This repository has been archived by the owner on Dec 22, 2024. It is now read-only.

feat/json_redis_sqlite_db_options #90

Merged
merged 12 commits into from
Dec 20, 2024
Merged

feat/json_redis_sqlite_db_options #90

merged 12 commits into from
Dec 20, 2024

Conversation

JarbasAl
Copy link
Member

@JarbasAl JarbasAl commented Jun 30, 2024

allow several database backends

Summary by CodeRabbit

  • New Features

    • Enhanced command-line interface with options for specifying database backends and Redis connection parameters.
    • Updated documentation to provide detailed command usage for hivemind-core.
  • Bug Fixes

    • Improved error handling for client addition failures in the command-line interface.
  • Refactor

    • Restructured client management and database interactions for better organization and clarity.
    • Centralized database access logic in client connection validation.
  • Documentation

    • Significant enhancements to the README, including a "Getting Started" section and detailed command descriptions.
  • Tests

    • Overhauled test suite for comprehensive coverage of client and database functionalities across different backends.

@JarbasAl JarbasAl added the enhancement New feature or request label Jun 30, 2024
@JarbasAl JarbasAl requested a review from goldyfruit June 30, 2024 21:18
@JarbasAl
Copy link
Member Author

@coderabbitai review

Copy link

coderabbitai bot commented Jul 16, 2024

Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

coderabbitai bot commented Jul 16, 2024

Warning

Rate limit exceeded

@JarbasAl has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 24 minutes and 14 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 7378541 and 6cb0874.

📒 Files selected for processing (2)
  • hivemind_core/scripts.py (14 hunks)
  • hivemind_core/version.py (1 hunks)

Walkthrough

The updates enhance the hivemind_core module by restructuring the README.md for better clarity on installation and usage, including new command-line options. The Client class is refactored into a dataclass, and an abstract database class is introduced with implementations for JSON, SQLite, and Redis. The command-line interface is improved to support new database configuration options, while the MessageBusEventHandler and HiveMindService classes are updated to streamline database interactions. Unit tests are expanded to cover a broader range of functionalities.

Changes

File Change Summary
README.md Restructured and enhanced content; added "Key Features", "Getting Started", and detailed command usage.
hivemind_core/database.py Refactored Client class to a dataclass, introduced AbstractDB, JsonDB, SQLiteDB, and RedisDB, and improved client management in ClientDatabase.
hivemind_core/protocol.py Enhanced HiveMindListenerProtocol to utilize a single ClientDatabase instance for database interactions.
hivemind_core/scripts.py Updated CLI commands to include options for database backend and Redis parameters; refined client data handling.
hivemind_core/service.py Added db attribute to MessageBusEventHandler, refactored client validation to use the db attribute.
setup.py Renamed package from "jarbas_hive_mind" to "hivemind-core" in the setup() function.
test/unittests/test_db.py Overhauled test suite to include multiple dedicated classes for testing client and database functionalities.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant ClientDatabase
    participant AbstractDB
    participant JsonDB
    participant SQLiteDB
    participant RedisDB

    User->>+ClientDatabase: Create or update client
    ClientDatabase->>+AbstractDB: Route to appropriate DB
    AbstractDB->>JsonDB: If JSON backend
    AbstractDB->>SQLiteDB: If SQLite backend
    AbstractDB->>RedisDB: If Redis backend
    JsonDB-->>-AbstractDB: Operation result
    SQLiteDB-->>-AbstractDB: Operation result
    RedisDB-->>-AbstractDB: Operation result
    AbstractDB-->>-ClientDatabase: Operation result
    ClientDatabase-->>-User: Confirm operation
Loading

Poem

In the hive of data, changes bloom,
Json, SQLite, Redis clear the room.
Clients dance, no longer confined,
Enhanced by tests, all intertwined.
Error messages now speak true,
With every update, fresh and new. 🐇📚


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: 1

Outside diff range, codebase verification and nitpick comments (4)
test/unittests/test_db.py (2)

39-53: New method test_update_entry checks client update functionality.

This method tests updating a client's name in the database. It's good to see comprehensive checks after the update to ensure the changes are reflected correctly. This method should also include assertions to verify that other client attributes are not unintentionally modified during the update process.

+            # Ensure other attributes are not changed
+            self.assertEqual(user.api_key, updated_user.api_key)
+            self.assertEqual(user.crypto_key, updated_user.crypto_key)

67-81: Edge cases for client ID reuse are not covered in test_delete_and_reuse_client_id.

The current test checks for deletion and reuse of a client ID but does not cover edge cases such as attempting to reuse an ID that has not been properly deleted. Consider adding tests to cover these scenarios to ensure database integrity and consistency.

Analysis chain

Testing deletion and reuse of client ID in test_delete_and_reuse_client_id.

This method tests important scenarios of deleting a client and ensuring that a new client can reuse the ID. It's crucial for database integrity and consistency. Ensure that the method covers all edge cases, such as attempting to reuse an ID that has not been properly deleted.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify edge cases for client ID reuse are tested.

# Test: Search for additional tests on client ID reuse.
rg --type python -A 5 $'test_delete_and_reuse_client_id' test/unittests/test_db.py

Length of output: 112


Script:

#!/bin/bash
# Description: Verify edge cases for client ID reuse are tested.

# Test: Search for the test_delete_and_reuse_client_id function definition and its context.
rg 'def test_delete_and_reuse_client_id' -A 10 test/unittests/test_db.py

# Test: Search for any other tests that might cover client ID reuse.
rg 'client_id' -A 10 test/unittests/test_db.py

Length of output: 1350

hivemind_core/database.py (2)

Line range hint 1-28: Refactoring of cast_to_client_obj to use decorator pattern.

The refactoring of cast_to_client_obj to use a decorator pattern is a good practice as it enhances modularity and reusability. The function now can handle different return types more robustly. However, the error message could be more specific by listing the actual unknown keys.

- raise RuntimeError(f"{func.__name__} returned a dict with unknown keys: {ret.keys()}")
+ unknown_keys = set(ret.keys()) - set(valid_kwargs)
+ raise RuntimeError(f"{func.__name__} returned a dict with unknown keys: {unknown_keys}")

Line range hint 257-346: Comprehensive handling of client operations in ClientDatabase.

The ClientDatabase class effectively abstracts the backend details and provides a unified interface for client operations. The error handling and use of the decorator pattern in methods like get_client_by_api_key and add_client are well-implemented. However, the method add_client could be refactored to avoid deep nesting and improve readability.

-        if user:
-            item_id = self.db.get_item_id(user)
-            if name:
-                user["name"] = name
-            if blacklist:
-                user["blacklist"] = blacklist
-            if allowed_types:
-                user["allowed_types"] = allowed_types
-            if admin is not None:
-                user["is_admin"] = admin
-            if crypto_key:
-                user["crypto_key"] = crypto_key
-            if password:
-                user["password"] = password
-            self.db.update_item(item_id, user)
-        else:
-            user = Client(
-                api_key=key,
-                name=name,
-                blacklist=blacklist,
-                crypto_key=crypto_key,
-                client_id=self.total_clients() + 1,
-                is_admin=admin,
-                password=password,
-                allowed_types=allowed_types,
-            )
-            self.db.add_item(user)
+        item_id = self.db.get_item_id(user) if user else None
+        user_data = {
+            "name": name,
+            "blacklist": blacklist,
+            "allowed_types": allowed_types,
+            "is_admin": admin,
+            "crypto_key": crypto_key,
+            "password": password
+        }
+        user_data = {k: v for k, v in user_data.items() if v is not None}
+        if item_id:
+            self.db.update_item(item_id, user_data)
+        else:
+            user = Client(api_key=key, **user_data, client_id=self.total_clients() + 1)
+            self.db.add_item(user)
Tools
Ruff

166-166: redis.commands.search.indexDefinition.IndexType imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


180-180: Do not use bare except

(E722)

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 0a1b723 and da1f90b.

Files selected for processing (2)
  • hivemind_core/database.py (9 hunks)
  • test/unittests/test_db.py (1 hunks)
Additional context used
Ruff
test/unittests/test_db.py

61-61: f-string without any placeholders

Remove extraneous f prefix

(F541)

hivemind_core/database.py

166-166: redis.commands.search.indexDefinition.IndexType imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


180-180: Do not use bare except

(E722)

Additional comments not posted (5)
test/unittests/test_db.py (3)

6-11: Refactoring to use unittest.TestCase and addition of setUp method.

The change from inheriting TestCase to unittest.TestCase is a good practice as it clarifies the source of TestCase. The addition of the setUp method is also beneficial as it centralizes the initialization of variables used across multiple test methods, enhancing readability and maintainability.


54-58: Testing for nonexistent entries in test_search_nonexistent_entry.

This method correctly tests the behavior of the system when searching for a nonexistent API key. It's crucial for robustness to ensure that the system handles such cases gracefully without errors.


13-38: Comprehensive testing in test_add_entry.

This method now thoroughly tests adding, searching, and deleting a client in the database. Using self for instance variables is a good practice as it ensures that these variables are accessible across different test methods. However, ensure that the database operations are mocked or handled in a way that they do not affect the actual database during testing.

hivemind_core/database.py (2)

Line range hint 47-86: Enhancements to Client class constructor with default values.

The addition of default values and optional parameters in the Client class constructor is a positive change, making the class more flexible and robust. The error handling in __setitem__ and __getitem__ methods is also well-implemented, providing clear feedback on errors.


105-133: Introduction of AbstractDB class and its concrete implementations.

The introduction of an abstract base class AbstractDB with defined abstract methods is a good architectural decision. It enforces a consistent interface for all database backends. The concrete implementations JsonDB and RedisDB are well-defined, adhering to the abstract base class's contract.

test/unittests/test_db.py Outdated Show resolved Hide resolved
@JarbasAl JarbasAl marked this pull request as ready for review December 20, 2024 03:43
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: 4

🔭 Outside diff range comments (1)
hivemind_core/scripts.py (1)

Line range hint 183-196: Potentially invalid update invocation in “delete_client”.
You are calling “db.update_item(item_id, dict(...))”, but the AbstractDB interface defines “update_item(client: Client) → bool”. Passing “item_id” and a dictionary doesn’t match that signature. This likely causes runtime issues.

Possible fix (example):

  1. Retrieve the client object via “get_client_by_api_key(...)” or iterate until you find the target,
  2. Update the client’s attributes, and
  3. Call “db.update_item(updated_client)”.
🧹 Nitpick comments (10)
test/unittests/test_db.py (4)

25-37: Consider verifying all serialized fields in tests.
In the “test_client_serialization” method, you are checking for the "client_id" field in the serialized output. It might be helpful to also verify the presence of other critical fields such as "api_key", "name", etc. This ensures that serialization is capturing all attributes properly.


74-85: Suggest verifying data after adding an item.
In “test_add_item” of TestJsonDB, after checking that the client ID exists in the database, consider also verifying whether the stored data matches all the client attributes.


99-112: Enhance coverage for “search_by_value” test.
Currently, “test_search_by_value” checks searching by "name". Consider adding additional queries (e.g., by "description" or "is_admin") to increase confidence in the search functionality.


150-161: Expand negative scenario testing in “test_delete_client”.
In addition to verifying a successful deletion scenario, consider adding a test where the client does not exist to ensure the method returns False (or behaves appropriately) in that situation.

hivemind_core/database.py (4)

29-30: Combine isinstance checks.
Per static analysis hint (SIM101), you could reduce duplication by combining the two isinstance checks into a single condition:

- if isinstance(ret, str) or isinstance(ret, dict):
+ if isinstance(ret, (str, dict)):
🧰 Tools
🪛 Ruff (0.8.2)

29-29: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


137-140: Avoid bare except and replace with context manager.
In “eq” method, the bare “except” can obscure debugging. Consider replacing with “contextlib.suppress” or at least specifying the exception type.

- try:
-     other = cast2client(other)
- except:
-     pass
+ from contextlib import suppress
+ with suppress(Exception):
+     other = cast2client(other)
🧰 Tools
🪛 Ruff (0.8.2)

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

Replace with contextlib.suppress(Exception)

(SIM105)


139-139: Do not use bare except

(E722)


345-346: Remove unused exception variable.
The local variable 'e' is never used in the exception block. You can remove it to avoid confusion.

- except Exception as e:
+ except Exception:
    LOG.error(f"Failed to save {self._db.path}")
    return False
🧰 Tools
🪛 Ruff (0.8.2)

345-345: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


416-416: Remove extraneous f-strings containing no placeholders.
You are using f-strings "f'client:*'" without any placeholders in lines 416 and 439. Please remove the “f” prefix to adhere to best practices.

-for client_id in self.redis.scan_iter(f"client:*"):
+for client_id in self.redis.scan_iter("client:*"):

Also applies to: 439-439

🧰 Tools
🪛 Ruff (0.8.2)

416-416: f-string without any placeholders

Remove extraneous f prefix

(F541)

hivemind_core/scripts.py (1)

64-65: Provide coverage for partial or update-only additions.
In “add_client”, when a matching client is found, you update some fields conditionally. Consider adding a dedicated test to confirm that partial updates are applied correctly (especially for admin, blacklists, etc.).

hivemind_core/service.py (1)

144-162: Consider adding type hints and docstring.

The client data retrieval block would benefit from type hints and documentation.

Consider this improvement:

-def open(self):
+def open(self) -> None:
+    """
+    Handle client connection and authentication.
+    
+    Validates the client's API key against the database and sets up client properties.
+    Closes the connection if validation fails.
+    """
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between da1f90b and e968cef.

📒 Files selected for processing (7)
  • README.md (1 hunks)
  • hivemind_core/database.py (1 hunks)
  • hivemind_core/protocol.py (3 hunks)
  • hivemind_core/scripts.py (13 hunks)
  • hivemind_core/service.py (5 hunks)
  • setup.py (1 hunks)
  • test/unittests/test_db.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
hivemind_core/database.py

29-29: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


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

Replace with contextlib.suppress(Exception)

(SIM105)


139-139: Do not use bare except

(E722)


345-345: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


416-416: f-string without any placeholders

Remove extraneous f prefix

(F541)


439-439: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (5)
test/unittests/test_db.py (1)

64-66: Good test coverage for list casting.
The “test_cast2client” method does a good job ensuring that casting a list of serialized clients results in a corresponding list of Client objects, maintaining data consistency.

README.md (1)

15-16: LGTM! Documentation updates are clear and comprehensive.

The documentation changes effectively communicate the command-line interface usage and available options.

hivemind_core/service.py (2)

205-205: LGTM! Database integration is properly handled.

The database parameter is correctly propagated through the service initialization.

Also applies to: 217-217, 220-220


267-267: LGTM! Protocol binding includes database access.

The protocol binding correctly includes the database parameter for consistent access across components.

hivemind_core/protocol.py (1)

256-256: LGTM! Database attribute properly typed.

The database attribute is correctly defined with proper typing.

setup.py Show resolved Hide resolved
hivemind_core/service.py Outdated Show resolved Hide resolved
hivemind_core/protocol.py Show resolved Hide resolved
hivemind_core/protocol.py Outdated Show resolved Hide resolved
allow several database backends

Update test/unittests/test_db.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 2

🧹 Nitpick comments (4)
hivemind_core/database.py (4)

35-38: Optimize type checking in cast2client function

The multiple isinstance checks can be merged for better readability and performance.

-    if isinstance(ret, str) or isinstance(ret, dict):
+    if isinstance(ret, (str, dict)):
         return Client.deserialize(ret)
🧰 Tools
🪛 Ruff (0.8.2)

35-35: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


348-353: Enhance error logging in commit method

The error message could be more informative by including the actual error details.

     try:
         self._db.store()
         return True
     except Exception as e:
-        LOG.error(f"Failed to save {self._db.path}")
+        LOG.error(f"Failed to save {self._db.path}: {str(e)}")
         return False
🧰 Tools
🪛 Ruff (0.8.2)

351-351: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


483-529: Consider splitting add_client method for better maintainability

The method handles both creation and update logic, making it complex. Consider splitting it into separate methods for better maintainability.

+    def _update_existing_client(self, user: Client, **kwargs) -> bool:
+        """Update an existing client with the provided attributes."""
+        for key, value in kwargs.items():
+            if value is not None:
+                setattr(user, key, value)
+        return self.db.update_item(user)
+
+    def _create_new_client(self, **kwargs) -> Client:
+        """Create a new client with the provided attributes."""
+        return Client(
+            client_id=self.total_clients() + 1,
+            **kwargs
+        )
+
     def add_client(self,
                    name: str,
                    key: str = "",
                    admin: bool = False,
                    intent_blacklist: Optional[List[str]] = None,
                    skill_blacklist: Optional[List[str]] = None,
                    message_blacklist: Optional[List[str]] = None,
                    allowed_types: Optional[List[str]] = None,
                    crypto_key: Optional[str] = None,
                    password: Optional[str] = None) -> bool:
         if crypto_key is not None:
             crypto_key = crypto_key[:16]
 
         user = self.get_client_by_api_key(key)
         if user:
-            # Update the existing client object directly
-            if name:
-                user.name = name
-            if intent_blacklist:
-                user.intent_blacklist = intent_blacklist
-            # ... more updates ...
-            return self.db.update_item(user)
+            return self._update_existing_client(user,
+                                              name=name,
+                                              intent_blacklist=intent_blacklist,
+                                              skill_blacklist=skill_blacklist,
+                                              message_blacklist=message_blacklist,
+                                              allowed_types=allowed_types,
+                                              is_admin=admin,
+                                              crypto_key=crypto_key,
+                                              password=password)
 
-        user = Client(
-            api_key=key,
-            name=name,
-            intent_blacklist=intent_blacklist,
-            # ... more attributes ...
-        )
+        user = self._create_new_client(
+            api_key=key,
+            name=name,
+            intent_blacklist=intent_blacklist,
+            skill_blacklist=skill_blacklist,
+            message_blacklist=message_blacklist,
+            crypto_key=crypto_key,
+            is_admin=admin,
+            password=password,
+            allowed_types=allowed_types)
         return self.db.add_item(user)

Line range hint 542-547: Improve error handling in context manager exit

The error handling in exit should be more specific about the type of error that occurred.

     def __exit__(self, _type, value, traceback):
         """Commits changes and Closes the session"""
         try:
             self.db.commit()
         except Exception as e:
-            LOG.error(e)
+            LOG.error(f"Failed to commit database changes on exit: {str(e)}")
+            # Consider if we should re-raise the exception
+            # raise
🧰 Tools
🪛 Ruff (0.8.2)

35-35: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


143-146: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


145-145: Do not use bare except

(E722)


351-351: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


423-423: f-string without any placeholders

Remove extraneous f prefix

(F541)


446-446: f-string without any placeholders

Remove extraneous f prefix

(F541)

🛑 Comments failed to post (2)
hivemind_core/database.py (2)

423-428: 🛠️ Refactor suggestion

Optimize Redis search implementation

The current implementation loads all clients and filters in memory. Consider using Redis SCAN with MATCH pattern for more efficient filtering.

     res = []
-    for client_id in self.redis.scan_iter(f"client:*"):
+    for client_id in self.redis.scan_iter("client:*"):
         client_data = self.redis.get(client_id)
         client = cast2client(client_data)
         if hasattr(client, key) and getattr(client, key) == val:
             res.append(client)
     return res

Consider implementing Redis search indices for better performance with large datasets.

📝 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.

        res = []
        for client_id in self.redis.scan_iter("client:*"):
            client_data = self.redis.get(client_id)
            client = cast2client(client_data)
            if hasattr(client, key) and getattr(client, key) == val:
                res.append(client)
        return res
🧰 Tools
🪛 Ruff (0.8.2)

423-423: f-string without any placeholders

Remove extraneous f prefix

(F541)


143-147: 🛠️ Refactor suggestion

Improve error handling in eq method

The bare except clause silently swallows all exceptions, which could hide potential issues.

     try:
         other = cast2client(other)
-    except:
+    except (TypeError, ValueError) as e:
+        LOG.debug(f"Failed to cast object for comparison: {e}")
         pass

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

🧰 Tools
🪛 Ruff (0.8.2)

143-146: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


145-145: Do not use bare except

(E722)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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: 6

🧹 Nitpick comments (11)
test/unittests/test_db.py (2)

114-146: Enhance Redis operation verification.

While the tests verify that Redis methods are called, they should also verify the actual data being stored/deleted.

 def test_add_item(self):
     client_data = {
         "client_id": 1,
         "api_key": "test_api_key",
         "name": "Test Client",
         "description": "A test client",
         "is_admin": False
     }
     client = Client(**client_data)
     self.db.add_item(client)
-    self.mock_redis.set.assert_called_once()
+    self.mock_redis.set.assert_called_once_with(
+        str(client.client_id),
+        client.serialize()
+    )

148-179: Add test cases for error conditions.

The test class should include cases for:

  • Deleting non-existent clients
  • Searching with invalid names
  • Database connection failures
def test_delete_nonexistent_client(self):
    db = MagicMock()
    db.delete_item.return_value = False
    client_db = ClientDatabase(backend="json")
    client_db.db = db
    client_db.get_client_by_api_key = MagicMock()
    client_db.get_client_by_api_key.return_value = None
    
    result = client_db.delete_client("nonexistent_key")
    self.assertFalse(result)
hivemind_core/service.py (1)

145-163: Consider enhancing crypto key security.

The crypto key handling could be improved:

  1. Add validation for minimum key length/complexity
  2. Consider implementing key rotation mechanism
  3. Add logging for failed crypto validation attempts
README.md (1)

229-233: Add security considerations for database backends.

Consider adding security recommendations for each backend:

  • JSON: File permissions and encryption at rest
  • SQLite: Access controls and backup strategy
  • Redis: Authentication, SSL/TLS configuration, and network security
hivemind_core/database.py (5)

103-103: Consider implementing the TODO comment using inspect.signature.

The TODO comment suggests filtering kwargs using inspect. This can be implemented to ensure only valid attributes are passed to the Client constructor.

Would you like me to provide an implementation using inspect.signature to filter the kwargs?


261-268: Reconsider the default implementation of commit.

The default implementation always returns True, which might give a false sense of success. Consider making it abstract or raising NotImplementedError to ensure implementations explicitly handle commits.

-    def commit(self) -> bool:
-        """
-        Commit changes to the database.
-
-        Returns:
-            True if the commit was successful, False otherwise.
-        """
-        return True
+    @abc.abstractmethod
+    def commit(self) -> bool:
+        """
+        Commit changes to the database.
+
+        Returns:
+            True if the commit was successful, False otherwise.
+        """
+        pass

538-538: Consider implementing RediSearch for efficient searching.

The TODO comment suggests adding proper search index support. RediSearch would provide efficient searching capabilities compared to the current linear scan approach.

Consider using RediSearch module to implement efficient searching:

  1. Create an index on client fields
  2. Use FT.SEARCH for efficient querying
  3. Implement secondary indices for frequently searched fields

381-402: Add indices for frequently searched columns.

Consider adding indices to improve search performance on commonly queried columns like api_key and name.

     def _initialize_database(self):
         """Initialize the database schema."""
         with self.conn:
             self.conn.execute("""
                 CREATE TABLE IF NOT EXISTS clients (
                     client_id INTEGER PRIMARY KEY,
                     api_key TEXT NOT NULL,
                     name TEXT,
                     description TEXT,
                     is_admin BOOLEAN DEFAULT FALSE,
                     last_seen REAL DEFAULT -1,
                     intent_blacklist TEXT,
                     skill_blacklist TEXT,
                     message_blacklist TEXT,
                     allowed_types TEXT,
                     crypto_key TEXT,
                     password TEXT,
                     can_broadcast BOOLEAN DEFAULT TRUE,
                     can_escalate BOOLEAN DEFAULT TRUE,
                     can_propagate BOOLEAN DEFAULT TRUE
                 )
             """)
+            # Add indices for frequently searched columns
+            self.conn.execute("CREATE INDEX IF NOT EXISTS idx_api_key ON clients(api_key)")
+            self.conn.execute("CREATE INDEX IF NOT EXISTS idx_name ON clients(name)")

354-359: Improve error logging in commit method.

The error message could be more informative by including the actual error details.

         try:
             self._db.store()
             return True
         except Exception as e:
-            LOG.error(f"Failed to save {self._db.path}")
+            LOG.error(f"Failed to save {self._db.path}: {str(e)}")
             return False
🧰 Tools
🪛 Ruff (0.8.2)

357-357: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

hivemind_core/scripts.py (2)

78-80: Improve error message for failed client addition.

The error message could be more informative by including the database backend information.

-            raise ValueError(f"Error adding User to database: {name}")
+            raise ValueError(f"Error adding user '{name}' to {db_backend} database")

46-47: Add type hints to function parameters.

Function parameters lack type hints, which would improve code maintainability and IDE support.

Example for one function:

-def add_client(name, access_key, password, crypto_key,
-               db_backend, db_name, db_folder, redis_host, redis_port):
+def add_client(
+    name: Optional[str],
+    access_key: Optional[str],
+    password: Optional[str],
+    crypto_key: Optional[str],
+    db_backend: str,
+    db_name: str,
+    db_folder: str,
+    redis_host: str,
+    redis_port: int
+) -> None:

Also applies to: 126-127, 209-210, 259-259, 413-414, 496-497, 578-579, 660-661

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c9684e8 and 64cd1ae.

📒 Files selected for processing (7)
  • README.md (1 hunks)
  • hivemind_core/database.py (1 hunks)
  • hivemind_core/protocol.py (3 hunks)
  • hivemind_core/scripts.py (13 hunks)
  • hivemind_core/service.py (5 hunks)
  • setup.py (1 hunks)
  • test/unittests/test_db.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • setup.py
  • hivemind_core/protocol.py
🧰 Additional context used
🪛 LanguageTool
README.md

[style] ~54-~54: Consider a shorter alternative to avoid wordiness.
Context: ...provide this info on the client devices in order to connect ### Running the Server Start ...

(IN_ORDER_TO_PREMIUM)

🪛 Markdownlint (0.37.0)
README.md

61-61: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


98-98: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


112-112: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


126-126: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


140-140: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


155-155: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


169-169: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


182-182: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


196-196: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


209-209: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

🪛 Gitleaks (8.21.2)
README.md

49-49: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 Ruff (0.8.2)
hivemind_core/service.py

140-140: Undefined name client

(F821)

hivemind_core/database.py

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


149-152: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


151-151: Do not use bare except

(E722)


357-357: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


591-591: f-string without any placeholders

Remove extraneous f prefix

(F541)


614-614: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (1)
test/unittests/test_db.py (1)

8-67: LGTM! Well-structured test coverage for Client class.

The test class provides comprehensive coverage of client operations including creation, serialization, and deserialization with consistent test data across methods.

test/unittests/test_db.py Outdated Show resolved Hide resolved
hivemind_core/service.py Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
hivemind_core/database.py Show resolved Hide resolved
hivemind_core/scripts.py Outdated Show resolved Hide resolved
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: 1

🧹 Nitpick comments (4)
README.md (4)

50-51: Enhance deprecation warning with migration guidance.

The warning about encryption key deprecation should include:

  1. When it will be fully deprecated
  2. How to migrate to password-based authentication
  3. Which client versions support the new authentication method

139-141: Enhance Redis backend example with connection details.

The Redis example should include:

  1. Connection parameters (host, port)
  2. Authentication details (if required)
  3. Expected output

Example:

$ hivemind-core allow-msg --db-backend redis --redis-host localhost --redis-port 6379
# Expected output:
Message types allowed: ['speak', 'recognize']
🧰 Tools
🪛 Markdownlint (0.37.0)

140-140: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


229-233: Add operational guidance for database backends.

Consider adding information about:

  1. Data persistence and backup strategies
  2. Performance characteristics and scaling limits
  3. Migration between backends
  4. Security considerations for each backend

225-254: Consider reordering sections for better flow.

The Protocol Support section should come before Database Backends since protocol version affects database interactions. This would provide readers with a better understanding of the system architecture before diving into specific implementations.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 64cd1ae and 3017749.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[style] ~54-~54: Consider a shorter alternative to avoid wordiness.
Context: ...provide this info on the client devices in order to connect ### Running the Server Start ...

(IN_ORDER_TO_PREMIUM)

🪛 Markdownlint (0.37.0)
README.md

61-61: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


98-98: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


112-112: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


126-126: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


140-140: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


155-155: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


169-169: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


182-182: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


196-196: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


209-209: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

🪛 Gitleaks (8.21.2)
README.md

49-49: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (1)
README.md (1)

41-52: Avoid exposing sensitive credentials in documentation.

The example output shows actual API keys and encryption keys. Consider using placeholder values like <access_key> instead of real credentials.

🧰 Tools
🪛 Gitleaks (8.21.2)

49-49: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

README.md Show resolved Hide resolved
Update README.md
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: 7

🧹 Nitpick comments (24)
hivemind_core/scripts.py (7)

12-26: Ensure robust error handling for unrecognized database backends.
Currently, if db_backend is neither "redis" nor one of the recognized file-based backends, it falls into the else block. Although click constrains the db_backend to "redis", "json", or "sqlite", adding a validation or fallback would improve code resilience.


71-74: Validate added client object.
After calling db.add_client, you validate success and raise a ValueError when it fails, but consider providing more context in the error message (e.g., reason for failure) for easier debugging.


157-167: Use consistent dictionary key access for client objects.
Here you are using x["client_id"] to check ID. In other places, you call client methods or attributes (e.g., client.client_id). Make sure to maintain consistency.


Line range hint 247-283: Provide user feedback on invalid database parameters.
Although you accept multiple database parameters, your code does not explicitly handle misconfiguration feedback. If, for example, Redis cannot connect, the user might not see a descriptive error. Consider adding a connection test or explicit warning messages.


Line range hint 356-409: Unblacklisting logic clarity
Ensure that removing a skill from the blacklist is the exact opposite of blacklisting it. If future expansions add special rules, consider unifying them behind a single function or method.


Line range hint 416-469: Apply consistent error messages for blacklisted items.
You print a generic error message when clients are already blacklisted or have an invalid API key. Keep those messages consistent across skill and intent blacklists for easier debugging.


Line range hint 476-529: Handle concurrency for unlocking multiple blocked clients.
When unblacklisting multiple intents, consider concurrency or partial success scenarios. Use a more granular approach if needed (e.g., track which ones were successfully unblacklisted).

test/unittests/test_db.py (6)

1-7: Remove unnecessary imports.
Currently, all imports (json, os, unittest, patch, MagicMock) look valid. However, if you do not use certain modules or constants, consider removing them to keep your test file lean.


72-78: Add locking or concurrency tests if needed.
For file-based JSON DB, read/write collisions could happen if multiple processes or threads access the file simultaneously. While that might be outside the scope, consider a test or note for concurrency usage.


105-117: Add coverage for advanced search queries.
The search_by_value function is tested with a single attribute. If you plan to expand search functionalities (OR, AND combos, partial matches, etc.), add relevant tests to confirm correct behavior.


121-138: Validate that Redis mocking covers all relevant Redis commands.
Here, you test set() in mock_redis but ensure other commands (like hset, get, or pipeline usage) are also properly tested if used in the actual code.


140-151: Check for Redis key collisions.
When adding an item with a particular key, you might want to test how the code handles collisions. If a key already exists in Redis, do you overwrite or error out? A short test for that scenario may help.


168-184: Extend get_clients_by_name tests for partial matches or case sensitivity.
With the current test, the exact name is used. If you eventually handle partial matches or case-insensitive searches, expand coverage to confirm complete functionality.

README.md (4)

1-5: Use shorter expressions to improve readability.
Your phrase “in order to connect” at line ~56 can be shortened to “to connect.” This minor edit clarifies style.


63-100: Consider removing dollar signs before commands.
Markdownlint warns you about using “$ command” lines without showing the actual output. If you don’t show command output in the snippet, remove the dollar sign for consistency.

🧰 Tools
🪛 Markdownlint (0.37.0)

63-63: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


100-100: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


220-224: Clarify distributed environment instructions.
The brief mention of using --ovos_bus_address and --ovos_bus_port is helpful, but you might want to note typical pitfalls, such as firewall or NAT configurations.


231-241: Consider specifying DB usage limits and caveats.
The table helps users pick a backend, but mention capacity or concurrency constraints (especially for JSON or SQLite) to guide them more effectively.

hivemind_core/service.py (5)

102-102: Expose DB handling to external classes or logs.
You’ve added db: Optional[ClientDatabase]. Consider providing brief logs or docstrings indicating how external classes can rely on this field’s lifecycle.


138-144: Improve error message for missing DB.
You currently raise a RuntimeError with a generic message. Provide details (like “Ensure the --db-backend is set properly and that the DB is accessible.”) to help developers fix the issue more easily.


145-151: Add a unit test verifying that invalid API keys close the connection.
You log “Client provided an invalid api key” and close the connection. Ensure you have a dedicated test verifying the service’s behavior in this exact scenario.


Line range hint 206-221: Inject database dependency.
You pass db as an optional parameter to HiveMindService. This is a neat approach to dependency injection. However, consider ensuring the system gracefully handles the scenario if a null DB is injected.


268-268: Extend handshake coverage in automated tests.
You call protocol.bind(...) with self.db. You might want an integration test confirming that handshake logic works with a real DB instance (especially with the handshake disabled scenario).

hivemind_core/database.py (2)

39-45: Optimize multiple isinstance checks

The multiple isinstance checks can be merged for better readability and performance.

-    if isinstance(ret, str) or isinstance(ret, dict):
+    if isinstance(ret, (str, dict)):
         return Client.deserialize(ret)
🧰 Tools
🪛 Ruff (0.8.2)

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


663-665: Move crypto_key truncation to Client class

The crypto_key truncation logic should be encapsulated within the Client class for better maintainability.

# In Client class
@property
def crypto_key(self) -> Optional[str]:
    return self._crypto_key

@crypto_key.setter
def crypto_key(self, value: Optional[str]):
    self._crypto_key = value[:16] if value else None
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3017749 and e2b026e.

📒 Files selected for processing (5)
  • README.md (1 hunks)
  • hivemind_core/database.py (1 hunks)
  • hivemind_core/scripts.py (14 hunks)
  • hivemind_core/service.py (5 hunks)
  • test/unittests/test_db.py (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[style] ~56-~56: Consider a shorter alternative to avoid wordiness.
Context: ...provide this info on the client devices in order to connect ### Running the Server Start ...

(IN_ORDER_TO_PREMIUM)

🪛 Markdownlint (0.37.0)
README.md

63-63: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


100-100: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


114-114: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


128-128: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


142-142: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


157-157: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


171-171: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


184-184: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


198-198: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


211-211: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

🪛 Gitleaks (8.21.2)
README.md

51-51: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 Ruff (0.8.2)
hivemind_core/database.py

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


149-152: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


151-151: Do not use bare except

(E722)


357-357: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


591-591: f-string without any placeholders

Remove extraneous f prefix

(F541)


614-614: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (9)
hivemind_core/scripts.py (4)

68-69: Check client naming fallback logic.
When the user does not provide a name, you generate a default name based on the total number of clients. If multiple operations run concurrently, there is a small possibility of a race condition. This is a minor concern, but you could mitigate it by re-checking or using an auto-increment approach.


115-122: Be mindful of partial iteration for large datasets.
Iterating and building big tables might be slow if there are many clients. It might be worth implementing pagination or a search approach if you anticipate large amounts of data.
[performance]


Line range hint 185-203: Consider thread safety when listing clients.
If the database can be accessed concurrently, ensure that the underlying DB code is thread-safe or locked properly to avoid inconsistent reads.


Line range hint 295-349: Prevent partial skill blacklist updates on script termination.
When processing a skill blacklist, if the script is interrupted after partial operations, some blacklists might remain uncommitted. Check if your DB concurrency model or close methods are robust enough to handle partial updates or transactions.

test/unittests/test_db.py (2)

91-103: Confirm item deletion success criteria.
The test checks self.db.delete_item(client) and asserts True. Ensure that you also confirm the item is indeed gone from the DB to minimize false positives (e.g., a scenario where the method returns True but item remains in DB).


153-166: Improve clarity of client_db mocking.
You apply a MagicMock in place of the real database. Make sure your test verifies the correct calls and that the logic aligns with actual usage. For instance, ensure that the code calls “delete_item” with the correct arguments.

README.md (1)

51-51: ⚠️ Potential issue

Mask example access keys when showing command output.
Static analysis flagged a “generic-api-key” at line 51. Replace it with a placeholder like <ACCESS_KEY> to avoid confusion and possible accidental credential exposure.

-Access Key: 42caf3d2405075fb9e7a4e1ff44e4c4f
+Access Key: <ACCESS_KEY>

Likely invalid or redundant comment.

🧰 Tools
🪛 Gitleaks (8.21.2)

51-51: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

hivemind_core/service.py (1)

152-158: Validate attribute assignment vs. storage.
You map multiple user attributes (crypto_key, message_blacklist, etc.) from the retrieved DB object to the client. Confirm your DB model aligns with the client object’s expected fields.

hivemind_core/database.py (1)

149-152: 🛠️ Refactor suggestion

Replace bare except with specific exception handling

The bare except clause can mask important errors and make debugging difficult.

-        try:
-            other = cast2client(other)
-        except:
-            pass
+        try:
+            other = cast2client(other)
+        except (TypeError, ValueError):
+            pass

Likely invalid or redundant comment.

🧰 Tools
🪛 Ruff (0.8.2)

149-152: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


151-151: Do not use bare except

(E722)

hivemind_core/scripts.py Outdated Show resolved Hide resolved
hivemind_core/scripts.py Outdated Show resolved Hide resolved
test/unittests/test_db.py Show resolved Hide resolved
README.md Show resolved Hide resolved
hivemind_core/database.py Outdated Show resolved Hide resolved
hivemind_core/database.py Show resolved Hide resolved
hivemind_core/database.py Show resolved Hide resolved
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: 0

🧹 Nitpick comments (2)
README.md (2)

7-7: Fix preposition in YouTube link text.

Change "in youtube" to "on YouTube" for correct English usage.

-Demo videos in [youtube](https://www.youtube.com/channel/UCYoV5kxp2zrH6pnoqVZpKSA/)
+Demo videos on [YouTube](https://www.youtube.com/channel/UCYoV5kxp2zrH6pnoqVZpKSA/)
🧰 Tools
🪛 LanguageTool

[uncategorized] ~7-~7: The preposition “on” seems more likely in this position than the preposition “in”.
Context: ... can't typically run OVOS. Demo videos in [youtube](https://www.youtube.com/chann...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_ON)


270-270: Fix abbreviation format.

The abbreviation "eg." should be "e.g." (with periods).

-run a [persona](https://github.com/OpenVoiceOS/ovos-persona) (eg. LLM). *text* input only
+run a [persona](https://github.com/OpenVoiceOS/ovos-persona) (e.g., LLM). *text* input only
🧰 Tools
🪛 LanguageTool

[uncategorized] ~270-~270: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...//github.com/OpenVoiceOS/ovos-persona) (eg. LLM). text input only ### Client Lib...

(E_G)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e2b026e and 6c4f534.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[uncategorized] ~7-~7: The preposition “on” seems more likely in this position than the preposition “in”.
Context: ... can't typically run OVOS. Demo videos in [youtube](https://www.youtube.com/chann...

(AI_EN_LECTOR_REPLACEMENT_PREPOSITION_IN_ON)


[style] ~56-~56: Consider a shorter alternative to avoid wordiness.
Context: ...provide this info on the client devices in order to connect ### Running the Server Start ...

(IN_ORDER_TO_PREMIUM)


[uncategorized] ~270-~270: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...//github.com/OpenVoiceOS/ovos-persona) (eg. LLM). text input only ### Client Lib...

(E_G)

🪛 Markdownlint (0.37.0)
README.md

63-63: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


100-100: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


114-114: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


128-128: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


142-142: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


157-157: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


171-171: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


184-184: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


198-198: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


211-211: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

🪛 Gitleaks (8.21.2)
README.md

51-51: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (2)
README.md (2)

43-53: Replace actual credentials with placeholders in example output.

The example output shows actual API keys and encryption keys. Use placeholder values instead.

 Node ID: 3
 Friendly Name: HiveMind-Node-2
-Access Key: 42caf3d2405075fb9e7a4e1ff44e4c4f
-Password: 5ae486f7f1c26bd4645bd052e4af3ea3
-Encryption Key: f46351c54f61a715
+Access Key: <access_key>
+Password: <password>
+Encryption Key: <encryption_key>
🧰 Tools
🪛 Gitleaks (8.21.2)

51-51: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


255-255: Enhance protocol compatibility documentation.

The note about protocol support should include more detailed information.

Consider adding:

  1. Timeline for Protocol v1 support in HiveMind-JS
  2. Compatibility matrix for all client libraries
  3. Migration guide from v0 to v1

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 (6)
test/unittests/test_db.py (2)

70-117: Consider expanding test coverage for JsonDB.

While the basic CRUD operations are covered, consider adding these test cases:

  • Verify all fields are correctly persisted in test_add_item
  • Verify the "revoked" state after deletion in test_delete_item
  • Test searching by different field types in test_search_by_value

153-184: Enhance TestClientDatabase coverage.

Consider adding tests for:

  • Database backend initialization with different configurations
  • Switching between different backends
  • Error handling for invalid backend configurations
hivemind_core/database.py (4)

11-19: Add type hints to optional dependency imports.

Consider adding type hints to clarify the types when dependencies are not available:

 try:
     import redis
 except ImportError:
-    redis = None
+    redis: Optional[Any] = None

 try:
     import sqlite3
 except ImportError:
-    sqlite3 = None
+    sqlite3: Optional[Any] = None

41-42: Optimize isinstance checks.

Merge the isinstance checks for better readability and performance:

-if isinstance(ret, str) or isinstance(ret, dict):
+if isinstance(ret, (str, dict)):
     return Client.deserialize(ret)
🧰 Tools
🪛 Ruff (0.8.2)

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


344-349: Fix error handling in JsonDB.commit.

Use the exception variable in the error message:

 try:
     self._db.store()
     return True
 except Exception as e:
-    LOG.error(f"Failed to save {self._db.path}")
+    LOG.error(f"Failed to save {self._db.path}: {e}")
     return False
🧰 Tools
🪛 Ruff (0.8.2)

347-347: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


598-608: Improve error messages for invalid backends.

Consider providing more helpful error messages:

-raise NotImplementedError(f"{backend} not supported, choose one of {self.valid_backends}")
+raise NotImplementedError(
+    f"Backend '{backend}' is not supported. "
+    f"Valid options are: {', '.join(self.valid_backends)}"
+)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6c4f534 and 255a865.

📒 Files selected for processing (2)
  • hivemind_core/database.py (1 hunks)
  • test/unittests/test_db.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
hivemind_core/database.py

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


347-347: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


556-556: f-string without any placeholders

Remove extraneous f prefix

(F541)


581-581: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (6)
test/unittests/test_db.py (2)

9-67: Well-structured test coverage for Client class.

The test methods comprehensively cover the essential functionality of the Client class, including object creation, serialization, deserialization, and type casting.


186-230: Comprehensive negative test cases.

The test class effectively covers important error conditions and edge cases, ensuring robust error handling in the Client class.

hivemind_core/database.py (4)

171-274: Well-designed abstract base class.

The AbstractDB class provides a solid foundation with:

  • Clear interface definition
  • Appropriate use of abstract methods
  • Helpful default implementations

439-446: ⚠️ Potential issue

Fix SQL injection vulnerability.

The current implementation is vulnerable to SQL injection:

-cur = self.conn.execute(f"SELECT * FROM clients WHERE {key} = ?", (val,))
+cur = self.conn.execute("SELECT * FROM clients WHERE ? = ?", (key, val))

Likely invalid or redundant comment.


153-156: ⚠️ Potential issue

Fix bare except clause.

The bare except clause can mask important errors. Use specific exception handling:

-try:
-    other = cast2client(other)
-except:
-    pass
+from contextlib import suppress
+with suppress(TypeError, ValueError):
+    other = cast2client(other)

Likely invalid or redundant comment.

🧰 Tools
🪛 Ruff (0.8.2)

153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


684-688: 🛠️ Refactor suggestion

Improve context manager error handling.

The current implementation swallows all exceptions. Consider:

 def __exit__(self, _type, value, traceback):
     try:
         self.db.commit()
-    except Exception as e:
-        LOG.error(e)
+    except Exception as e:
+        LOG.error(f"Failed to commit database changes: {e}")
+        return False  # Re-raise the exception
+    return True

Likely invalid or redundant comment.

test/unittests/test_db.py Show resolved Hide resolved
hivemind_core/database.py Show resolved Hide resolved
@JarbasAl
Copy link
Member Author

closes #3

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: 1

♻️ Duplicate comments (1)
hivemind_core/database.py (1)

439-446: ⚠️ Potential issue

Critical: SQL injection vulnerability in search_by_value method.

The current implementation is vulnerable to SQL injection attacks by directly interpolating the key parameter into the SQL query.

This issue was previously identified and tracked in issue #104. The fix needs to be implemented:

-                cur = self.conn.execute(f"SELECT * FROM clients WHERE {key} = ?", (val,))
+                cur = self.conn.execute("SELECT * FROM clients WHERE ? = ?", (key, val))
🧹 Nitpick comments (3)
hivemind_core/database.py (3)

41-42: Optimize isinstance checks for better performance.

Multiple isinstance checks can be merged into a single call for better readability and performance.

-if isinstance(ret, str) or isinstance(ret, dict):
+if isinstance(ret, (str, dict)):
     return Client.deserialize(ret)
🧰 Tools
🪛 Ruff (0.8.2)

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


510-510: Implement proper search index support.

The TODO comment indicates that proper search index support is missing. This could impact search performance at scale.

Would you like me to help implement a proper search index solution using RediSearch?


638-671: Separate update and create operations in add_client method.

The method currently handles both creating new clients and updating existing ones, making it complex and harder to maintain.

Consider splitting this into two separate methods:

def update_client(self, key: str, **updates) -> bool:
    user = self.get_client_by_api_key(key)
    if not user:
        return False
    
    for attr, value in updates.items():
        if value is not None:
            setattr(user, attr, value)
    
    return self.db.update_item(user)

def create_client(self, key: str, name: str, **kwargs) -> bool:
    if self.get_client_by_api_key(key):
        return False
        
    user = Client(
        api_key=key,
        name=name,
        client_id=self.total_clients() + 1,
        **kwargs
    )
    return self.db.add_item(user)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 255a865 and 2585235.

📒 Files selected for processing (1)
  • hivemind_core/database.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
hivemind_core/database.py

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


347-347: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


556-556: f-string without any placeholders

Remove extraneous f prefix

(F541)


581-581: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (1)
hivemind_core/database.py (1)

153-156: 🛠️ Refactor suggestion

Improve error handling in equality comparison.

The bare except clause can mask important errors. Use contextlib.suppress or catch specific exceptions.

+from contextlib import suppress
+
-        try:
-            other = cast2client(other)
-        except:
-            pass
+        with suppress(TypeError, ValueError):
+            other = cast2client(other)

Likely invalid or redundant comment.

🧰 Tools
🪛 Ruff (0.8.2)

153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)

hivemind_core/database.py Show resolved Hide resolved
@JarbasAl JarbasAl changed the title feat/redis_db feat/json_redis_sqlite_db_options Dec 20, 2024
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

♻️ Duplicate comments (1)
hivemind_core/database.py (1)

440-447: ⚠️ Potential issue

SQL injection vulnerability in search_by_value.

The current implementation is vulnerable to SQL injection attacks by directly interpolating the key parameter into the SQL query.

This issue has been tracked in #104. Please prioritize fixing this security vulnerability.

🧹 Nitpick comments (5)
test/unittests/test_db.py (2)

11-67: Consider adding edge cases to client tests.

The client tests cover basic functionality well, but could benefit from additional edge cases:

  • Unicode characters in name/description
  • Empty strings for optional fields
  • Maximum length validation for fields

188-230: Add test for concurrent type validation.

The negative test cases should include scenarios where multiple invalid fields are provided simultaneously:

def test_multiple_invalid_fields(self):
    client_data = {
        "client_id": "invalid_id",
        "api_key": 123,  # should be string
        "is_admin": "not_boolean"
    }
    with self.assertRaises(ValueError):
        Client(**client_data)
hivemind_core/service.py (1)

137-142: Improve error message for database initialization failure.

The error message could be more specific about the configuration requirements:

-LOG.error("Database connection not initialized. Please ensure database configuration is correct.")
+LOG.error("Database connection not initialized. Verify database backend configuration (--db-backend, --db-name) and connection parameters.")
README.md (1)

270-270: Fix abbreviation format.

Correct the abbreviation format:

-run a [persona](https://github.com/OpenVoiceOS/ovos-persona) (eg. LLM)
+run a [persona](https://github.com/OpenVoiceOS/ovos-persona) (e.g., LLM)
🧰 Tools
🪛 LanguageTool

[uncategorized] ~270-~270: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...//github.com/OpenVoiceOS/ovos-persona) (eg. LLM). text input only ### Client Lib...

(E_G)

hivemind_core/database.py (1)

39-45: Optimize multiple isinstance checks.

Multiple isinstance checks can be combined for better readability and performance.

Apply this diff:

-    if ret is None or isinstance(ret, Client):
-        return ret
-    if isinstance(ret, str) or isinstance(ret, dict):
+    if ret is None or isinstance(ret, Client):
+        return ret
+    if isinstance(ret, (str, dict)):
🧰 Tools
🪛 Ruff (0.8.2)

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2585235 and d5ffc90.

📒 Files selected for processing (5)
  • README.md (1 hunks)
  • hivemind_core/database.py (1 hunks)
  • hivemind_core/protocol.py (3 hunks)
  • hivemind_core/service.py (4 hunks)
  • test/unittests/test_db.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
hivemind_core/database.py

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


348-348: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


557-557: f-string without any placeholders

Remove extraneous f prefix

(F541)


582-582: f-string without any placeholders

Remove extraneous f prefix

(F541)

🪛 LanguageTool
README.md

[style] ~56-~56: Consider a shorter alternative to avoid wordiness.
Context: ...provide this info on the client devices in order to connect ### Running the Server Start ...

(IN_ORDER_TO_PREMIUM)


[uncategorized] ~270-~270: The abbreviation “e.g.” (= for example) requires two periods.
Context: ...//github.com/OpenVoiceOS/ovos-persona) (eg. LLM). text input only ### Client Lib...

(E_G)

🪛 Markdownlint (0.37.0)
README.md

63-63: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


100-100: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


114-114: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


128-128: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


142-142: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


157-157: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


171-171: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


184-184: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


198-198: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)


211-211: null
Dollar signs used before commands without showing output

(MD014, commands-show-output)

🪛 Gitleaks (8.21.2)
README.md

51-51: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (6)
README.md (1)

48-53: ⚠️ Potential issue

Replace sensitive credentials with placeholders.

The example output shows actual API keys and encryption keys. Use placeholders instead:

-Node ID: 3
-Friendly Name: HiveMind-Node-2
-Access Key: 42caf3d2405075fb9e7a4e1ff44e4c4f
-Password: 5ae486f7f1c26bd4645bd052e4af3ea3
-Encryption Key: f46351c54f61a715
+Node ID: <id>
+Friendly Name: <name>
+Access Key: <access_key>
+Password: <password>
+Encryption Key: <encryption_key>

Likely invalid or redundant comment.

🧰 Tools
🪛 Gitleaks (8.21.2)

51-51: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

hivemind_core/database.py (3)

153-156: Fix bare except clause.

The bare except clause can mask important errors and make debugging difficult.

Apply this diff to catch specific exceptions:

-        try:
-            other = cast2client(other)
-        except:
-            pass
+        from contextlib import suppress
+        with suppress(TypeError, ValueError):
+            other = cast2client(other)
🧰 Tools
🪛 Ruff (0.8.2)

153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


557-564: Optimize Redis search performance.

The current implementation performs a full scan of keys which can be inefficient for large datasets.

Consider using Redis sets for indexing additional fields beyond just name and api_key.

🧰 Tools
🪛 Ruff (0.8.2)

557-557: f-string without any placeholders

Remove extraneous f prefix

(F541)


573-573: Improve reliability of client count calculation.

The current implementation assumes a fixed ratio of index entries, which is brittle and could lead to incorrect counts if the indexing strategy changes.

This issue has been tracked in #105.

hivemind_core/protocol.py (2)

266-268: 🛠️ Refactor suggestion

Add error handling for database binding.

The bind method should validate the database parameter and handle potential connection issues.

Apply this diff:

-    def bind(self, websocket, bus, identity, db: ClientDatabase):
+    def bind(self, websocket, bus, identity, db: ClientDatabase) -> None:
+        """
+        Bind the protocol to required components.
+        
+        Args:
+            websocket: The websocket handler
+            bus: The message bus client
+            identity: The node identity
+            db: The client database instance
+        
+        Raises:
+            ValueError: If database instance is None
+        """
+        if db is None:
+            raise ValueError("Database instance cannot be None")
         self.identity = identity
         self.db = db
         websocket.protocol = self

Likely invalid or redundant comment.


760-763: 🛠️ Refactor suggestion

Add error handling for database queries.

The blacklist update logic should handle potential database query failures gracefully.

Apply this diff:

-        user = self.db.get_client_by_api_key(client.key)
-        client.skill_blacklist = user.skill_blacklist or []
-        client.intent_blacklist = user.intent_blacklist or []
-        client.msg_blacklist = user.message_blacklist or []
+        try:
+            user = self.db.get_client_by_api_key(client.key)
+            if user is None:
+                LOG.error(f"Failed to retrieve user data for client {client.peer}")
+                return message
+            client.skill_blacklist = user.skill_blacklist or []
+            client.intent_blacklist = user.intent_blacklist or []
+            client.msg_blacklist = user.message_blacklist or []
+        except Exception as e:
+            LOG.error(f"Database error while updating blacklist for client {client.peer}: {e}")
+            return message

Likely invalid or redundant comment.

Comment on lines +121 to +151
@patch('hivemind_core.database.redis.StrictRedis')
def setUp(self, MockRedis):
self.mock_redis = MagicMock()
MockRedis.return_value = self.mock_redis
self.db = RedisDB()

def test_add_item(self):
client_data = {
"client_id": 1,
"api_key": "test_api_key",
"name": "Test Client",
"description": "A test client",
"is_admin": False
}
client = Client(**client_data)
self.db.add_item(client)
self.mock_redis.set.assert_called_once()

def test_delete_item(self):
client_data = {
"client_id": 1,
"api_key": "test_api_key",
"name": "Test Client",
"description": "A test client",
"is_admin": False
}
client = Client(**client_data)
self.db.add_item(client)
result = self.db.delete_item(client)
self.assertTrue(result)

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance Redis mock assertions.

The Redis tests could be more thorough in verifying the exact Redis operations:

 def test_add_item(self):
     client_data = {
         "client_id": 1,
         "api_key": "test_api_key",
         "name": "Test Client",
         "description": "A test client",
         "is_admin": False
     }
     client = Client(**client_data)
     self.db.add_item(client)
-    self.mock_redis.set.assert_called_once()
+    self.mock_redis.set.assert_called_once_with(
+        f"client:{client.client_id}",
+        client.serialize()
+    )
📝 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
@patch('hivemind_core.database.redis.StrictRedis')
def setUp(self, MockRedis):
self.mock_redis = MagicMock()
MockRedis.return_value = self.mock_redis
self.db = RedisDB()
def test_add_item(self):
client_data = {
"client_id": 1,
"api_key": "test_api_key",
"name": "Test Client",
"description": "A test client",
"is_admin": False
}
client = Client(**client_data)
self.db.add_item(client)
self.mock_redis.set.assert_called_once()
def test_delete_item(self):
client_data = {
"client_id": 1,
"api_key": "test_api_key",
"name": "Test Client",
"description": "A test client",
"is_admin": False
}
client = Client(**client_data)
self.db.add_item(client)
result = self.db.delete_item(client)
self.assertTrue(result)
@patch('hivemind_core.database.redis.StrictRedis')
def setUp(self, MockRedis):
self.mock_redis = MagicMock()
MockRedis.return_value = self.mock_redis
self.db = RedisDB()
def test_add_item(self):
client_data = {
"client_id": 1,
"api_key": "test_api_key",
"name": "Test Client",
"description": "A test client",
"is_admin": False
}
client = Client(**client_data)
self.db.add_item(client)
self.mock_redis.set.assert_called_once_with(
f"client:{client.client_id}",
client.serialize()
)
def test_delete_item(self):
client_data = {
"client_id": 1,
"api_key": "test_api_key",
"name": "Test Client",
"description": "A test client",
"is_admin": False
}
client = Client(**client_data)
self.db.add_item(client)
result = self.db.delete_item(client)
self.assertTrue(result)

test/unittests/test_db.py Show resolved Hide resolved
hivemind_core/service.py Show resolved Hide resolved
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)
hivemind_core/scripts.py (1)

13-28: Enhance docstring and add parameter validation.

The function is well-structured, but could benefit from:

  1. Detailed parameter descriptions in the docstring
  2. Validation for the redis_password parameter
 def get_db_kwargs(db_backend: str, db_name: str, db_folder: str,
                  redis_host: str, redis_port: int, redis_password: Optional[str]) -> dict:
-    """Get database configuration kwargs based on backend type."""
+    """Get database configuration kwargs based on backend type.
+    
+    Args:
+        db_backend: The database backend type ('redis', 'json', or 'sqlite')
+        db_name: The name of the database file for json/sqlite backends
+        db_folder: The folder where database files are stored for json/sqlite backends
+        redis_host: The Redis server host
+        redis_port: The Redis server port
+        redis_password: Optional Redis server password
+    
+    Returns:
+        dict: Configuration kwargs for the selected database backend
+    """
     kwargs = {"backend": db_backend}
     if db_backend == "redis":
+        if redis_password is not None and not isinstance(redis_password, str):
+            raise ValueError("redis_password must be None or a string")
         kwargs.update({
             "host": redis_host,
             "port": redis_port,
             "password": redis_password
         })
hivemind_core/database.py (2)

107-107: Address TODO comment about filtering kwargs.

The TODO comment about filtering kwargs with inspect should be implemented to prevent invalid attribute assignments.

Would you like me to help implement the kwargs filtering using inspect.signature() to ensure only valid attributes are passed to the Client constructor?


41-41: Address static analysis issues.

Several code quality issues were identified:

  1. Multiple isinstance calls can be merged
  2. Unused exception variable
  3. f-strings without placeholders
-    if isinstance(ret, str) or isinstance(ret, dict):
+    if isinstance(ret, (str, dict)):

-        except Exception as e:
+        except Exception:

-        for client_id in self.redis.scan_iter(f"client:*"):
+        for client_id in self.redis.scan_iter("client:*"):

-        for client_id in self.redis.scan_iter(f"client:*"):
+        for client_id in self.redis.scan_iter("client:*"):

Also applies to: 348-348, 559-559, 584-584

🧰 Tools
🪛 Ruff (0.8.2)

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d5ffc90 and 7378541.

📒 Files selected for processing (2)
  • hivemind_core/database.py (1 hunks)
  • hivemind_core/scripts.py (14 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
hivemind_core/database.py

41-41: Multiple isinstance calls for ret, merge into a single call

Merge isinstance calls for ret

(SIM101)


153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


348-348: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


559-559: f-string without any placeholders

Remove extraneous f prefix

(F541)


584-584: f-string without any placeholders

Remove extraneous f prefix

(F541)

🔇 Additional comments (3)
hivemind_core/database.py (3)

575-575: ⚠️ Potential issue

Improve reliability of client count calculation.

The current implementation assumes a fixed ratio of index entries (dividing by 3), which is brittle and could lead to incorrect counts if the indexing strategy changes.

-        return int(len(self.redis.keys("client:*")) / 3)  # because of index entries for name/key fastsearch
+        return len([k for k in self.redis.scan_iter("client:*") if not k.startswith("client:index:")])

Likely invalid or redundant comment.


153-156: ⚠️ Potential issue

Replace bare except with specific exception handling.

Using a bare except clause can mask important errors and make debugging harder.

     try:
         other = cast2client(other)
-    except:
+    except (TypeError, ValueError):
         pass

Likely invalid or redundant comment.

🧰 Tools
🪛 Ruff (0.8.2)

153-156: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


155-155: Do not use bare except

(E722)


442-442: ⚠️ Potential issue

Fix SQL injection vulnerability in search_by_value.

The current implementation is vulnerable to SQL injection attacks by directly interpolating the key parameter into the SQL query.

-                cur = self.conn.execute(f"SELECT * FROM clients WHERE {key} = ?", (val,))
+                cur = self.conn.execute("SELECT * FROM clients WHERE ? = ?", (key, val))

Likely invalid or redundant comment.

Comment on lines 41 to 49
@click.option("--db-backend", type=click.Choice(['redis', 'json', 'sqlite'], case_sensitive=False), default='json',
help="Select the database backend to use. Options: redis, sqlite, json.")
@click.option("--db-name", type=str, default="clients",
help="[json/sqlite] The name for the database file. ~/.cache/hivemind-core/{name}")
@click.option("--db-folder", type=str, default="hivemind-core",
help="[json/sqlite] The subfolder where database files are stored. ~/.cache/{db_folder}}")
@click.option("--redis-host", default="localhost", help="[redis] Host for Redis. Default is localhost.")
@click.option("--redis-port", default=6379, help="[redis] Port for Redis. Default is 6379.")
@click.option("--redis-password", required=FileNotFoundError, help="[redis] Password for Redis. Default None")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reduce code duplication by extracting database options into a shared decorator.

The database configuration options are repeated across all commands. This violates the DRY principle and makes maintenance harder.

Create a decorator to share these options:

def add_db_options(f):
    """Add database configuration options to a click command."""
    options = [
        click.option("--db-backend", 
                    type=click.Choice(['redis', 'json', 'sqlite'], case_sensitive=False),
                    default='json',
                    help="Select the database backend to use. Options: redis, sqlite, json."),
        click.option("--db-name", 
                    type=str, 
                    default="clients",
                    help="[json/sqlite] The name for the database file. ~/.cache/hivemind-core/{name}"),
        click.option("--db-folder", 
                    type=str, 
                    default="hivemind-core",
                    help="[json/sqlite] The subfolder where database files are stored. ~/.cache/{db_folder}}"),
        click.option("--redis-host", 
                    default="localhost",
                    help="[redis] Host for Redis. Default is localhost."),
        click.option("--redis-port", 
                    default=6379,
                    help="[redis] Port for Redis. Default is 6379."),
        click.option("--redis-password",
                    required=False,
                    help="[redis] Password for Redis. Default None")
    ]
    for option in reversed(options):
        f = option(f)
    return f

Then use it on commands:

@hmcore_cmds.command(help="add credentials for a client", name="add-client")
@add_db_options
def add_client(...):

Also applies to: 100-108, 162-170, 191-199, 254-262, 293-301, 355-363, 416-424, 477-485

print(f"Blacklisted '{skill_id}' for {client['name']}")
client.skill_blacklist.remove(skill_id)
db.update_item(client)
print(f"Blacklisted '{skill_id}' for {client.name}")
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 incorrect success message in unblacklist_intent.

The success message incorrectly states that the intent was blacklisted when it was actually unblacklisted.

-                print(f"Blacklisted '{skill_id}' for {client.name}")
+                print(f"Unblacklisted '{skill_id}' for {client.name}")
📝 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
print(f"Blacklisted '{skill_id}' for {client.name}")
print(f"Unblacklisted '{skill_id}' for {client.name}")

Comment on lines +171 to +172
def delete_client(node_id, db_name, db_folder,
db_backend, redis_host, redis_port, redis_password):
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 parameter order in delete_client function signature.

The parameters are in the wrong order compared to their usage in the function call to get_db_kwargs.

-def delete_client(node_id, db_name, db_folder,
-                  db_backend, redis_host, redis_port, redis_password):
+def delete_client(node_id, db_backend, db_name, db_folder,
+                  redis_host, redis_port, redis_password):
📝 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
def delete_client(node_id, db_name, db_folder,
db_backend, redis_host, redis_port, redis_password):
def delete_client(node_id, db_backend, db_name, db_folder,
redis_host, redis_port, redis_password):

@JarbasAl JarbasAl merged commit 79a3ceb into dev Dec 20, 2024
1 of 2 checks passed
@JarbasAl JarbasAl deleted the feat/redis_db branch December 20, 2024 20:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant