langgenius / dify

Dify is an open-source LLM app development platform. Dify's intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production.
https://dify.ai
Other
43.49k stars 6.03k forks source link

Cannot load custom Moderation extension #3981

Open lo5twind opened 4 months ago

lo5twind commented 4 months ago

Self Checks

Dify version

0.6.5

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

When I wrote a custom Moderation extension according to the official documentation, a loading error occurred during the restart of the service. The error message is as follows:

Traceback (most recent call last):
  File "/usr/local/bin/flask", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.10/site-packages/flask/cli.py", line 1105, in main
    cli.main()
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1682, in invoke
    cmd_name, cmd, args = self.resolve_command(ctx, args)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1729, in resolve_command
    cmd = self.get_command(ctx, cmd_name)
  File "/usr/local/lib/python3.10/site-packages/flask/cli.py", line 611, in get_command
    app = info.load_app()
  File "/usr/local/lib/python3.10/site-packages/flask/cli.py", line 335, in load_app
    app = locate_app(import_name, name)
  File "/usr/local/lib/python3.10/site-packages/flask/cli.py", line 245, in locate_app
    __import__(module_name)
  File "/app/api/app.py", line 210, in <module>
    app = create_app()
  File "/app/api/app.py", line 105, in create_app
    initialize_extensions(app)
  File "/app/api/app.py", line 116, in initialize_extensions
    ext_code_based_extension.init()
  File "/app/api/extensions/ext_code_based_extension.py", line 5, in init
    code_based_extension.init()
  File "/app/api/core/extension/extension.py", line 16, in init
    self.__module_extensions[module.value] = module_class.scan_extensions()
  File "/app/api/core/extension/extensible.py", line 111, in scan_extensions
    sorted_extensions = sort_to_dict_by_position_map(position_map, extensions, lambda x: x.name)
  File "/app/api/core/utils/position_helper.py", line 69, in sort_to_dict_by_position_map
    sorted_items = sort_by_position_map(position_map, data, name_func)
  File "/app/api/core/utils/position_helper.py", line 53, in sort_by_position_map
    return sorted(data, key=lambda x: position_map.get(name_func(x), float('inf')))
TypeError: '<' not supported between instances of 'NoneType' and 'int'

When I check the source code, I found that a non-builtin extension will set a None in position_map that will make this TypeError, please help to fix that.

I try to modify the code at api/core/extension/extensible.py line 68, like below:

                if '__builtin__' in file_names:
                    builtin = True

                    builtin_file_path = os.path.join(subdir_path, '__builtin__')
                    if os.path.exists(builtin_file_path):
                        with open(builtin_file_path, encoding='utf-8') as f:
                            position = int(f.read().strip())
                    position_map[extension_name] = position                  # modified work
                #position_map[extension_name] = position                   # original  NOT work

✔️ Expected Behavior

load extension success

❌ Actual Behavior

TypeError exception was thrown.

dosubot[bot] commented 4 months ago

To resolve the "TypeError: '<' not supported between instances of 'NoneType' and 'int'" error you're encountering with your custom Moderation extension, you should utilize the _position.yaml configuration for managing extension positions. This approach eliminates the need to manually set positions in the code, which is causing the error due to a None value in position_map.

Here's what you need to do:

  1. Ensure _position.yaml Exists: Create or update the _position.yaml file in the directory with your extensions. This file should list the names of all extensions, including your custom one, in the desired load order.

  2. Match Extension Names: The names in _position.yaml must exactly match your extension names. These names are typically the directory names of the extensions or a specified identifier within the extension.

  3. Format and Placement: Verify that _position.yaml is correctly formatted and placed. Incorrect formatting or placement can lead to errors.

By following these steps, you should be able to load your custom Moderation extension successfully without encountering the TypeError. This method simplifies extension position management and avoids manual position assignments in the Python code, addressing the root cause of the error you've experienced.

#### Details **Sources** - [api/core/extension/extensible.py](https://github.com/langgenius/dify/blob/main/api/core/extension/extensible.py) - [api/core/utils/position_helper.py](https://github.com/langgenius/dify/blob/main/api/core/utils/position_helper.py)
**About Dosu** This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

To continue the conversation, mention @dosu.

lo5twind commented 4 months ago

@dosu It seems that _position.yaml is only used in model provider, not with code base extensions.

dosubot[bot] commented 4 months ago

To address the issue with loading a custom Moderation extension and the TypeError: '<' not supported between instances of 'NoneType' and 'int' error in Dify version 0.6.5, you can add a position attribute directly in your custom extension class. This attribute will specify the order in which the extension should be loaded, avoiding the comparison with None.

Here's an example of how to implement this in your custom extension class:

class YourCustomModerationExtension:
    # Define a position attribute with an integer value
    position: int = 10  # Adjust the value based on the desired load order

    # Your existing extension code...

By setting a specific position value, you ensure that your extension can be sorted and loaded correctly among other extensions. This method circumvents the issue caused by a None value in position_map for non-builtin extensions.

However, be cautious with this approach as it could lead to sorting conflicts, inconsistencies with built-in extensions, and maintenance challenges, especially if the load order needs to be adjusted in the future or if Dify's handling of extension ordering changes. Always ensure that the position values are unique and carefully managed to avoid conflicts with other extensions.

#### Details **Sources** - [zh_CN/guides/application-design/extension/code_based_extension/moderation.md](https://github.com/langgenius/dify-docs/blob/main/zh_CN/guides/application-design/extension/code_based_extension/moderation.md) - [api/core/extension/extensible.py](https://github.com/langgenius/dify/blob/main/api/core/extension/extensible.py) - [api/core/utils/position_helper.py](https://github.com/langgenius/dify/blob/main/api/core/utils/position_helper.py) - [api/core/tools/provider/builtin/_positions.py](https://github.com/langgenius/dify/blob/main/api/core/tools/provider/builtin/_positions.py)
**About Dosu** This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

To continue the conversation, mention @dosu.

lo5twind commented 4 months ago

@dosu The position variable is not update in position_map if extension is not buildin. So no matter what you add within CustomExtension, it will not work. To solve this problem, the only way is to modify the code, not the CustomExtension

dosubot[bot] commented 4 months ago

To effectively solve the problem where the position variable is not updated in position_map for non-__builtin__ extensions in Dify, leading to a TypeError, you need to modify the code to ensure that all extensions, regardless of being __builtin__ or not, have their positions properly recognized and sorted. This involves creating or updating a _position.yaml file for managing the load order of extensions and adjusting the code to read this configuration.

Here's a concise approach to address the issue:

  1. Create or Update _position.yaml: For each extension, including non-__builtin__ ones, ensure there's a _position.yaml file in their directory specifying the load order. This file should map extension names to their desired positions.

  2. Adjust Code to Use _position.yaml: Modify the extension loading mechanism to read the _position.yaml file and create a position_map that includes all extensions with their specified positions. If an extension does not have a position specified, it should default to being placed at the end of the load order.

  3. Implement Sorting Logic: Use the sort_by_position_map function, ensuring it handles cases where an extension's position might be undefined by assigning a default value that places it at the end of the list. This prevents the TypeError by avoiding comparisons between NoneType and int.

By following these steps, you ensure that every extension, whether __builtin__ or custom, is accounted for in the position_map with a valid position, thus resolving the sorting issue and preventing the TypeError. This method provides a scalable and maintainable way to manage extension load order in Dify.

#### Details **Sources** - [api/core/extension/extensible.py](https://github.com/langgenius/dify/blob/main/api/core/extension/extensible.py) - [api/core/utils/position_helper.py](https://github.com/langgenius/dify/blob/main/api/core/utils/position_helper.py) - [api/extensions/ext_sentry.py](https://github.com/langgenius/dify/blob/main/api/extensions/ext_sentry.py)
**About Dosu** This response is meant to be useful and save you time. It is **not meant to be a precise solution**, but rather a starting point for your own research. Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

To continue the conversation, mention @dosu.

yuhp-zts commented 2 months ago

May I see how you have configured the moderation?

qjy706 commented 1 month ago

@lo5twind Hi, have you finally solved it