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
51.16k stars 7.37k forks source link

Custom tool json array output is parsed as string #6606

Closed elvis-cai closed 3 months ago

elvis-cai commented 3 months ago

Self Checks

Dify version

0.6.11

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

deploy a custom tool to return json array and test the custom tool to see the output, it's returning text string instead of json format

image

✔️ Expected Behavior

json output is parsed correctly instread of text string format

❌ Actual Behavior


{
  "text": "[{\"description\": \"The image depicts a scenic view of a grassy field with a wooden walkway or boardwalk leading through it. The sky above is mostly clear with some clouds, and there are trees and shrubs visible on either side of the walkway. The overall scene conveys a peaceful, natural environment, likely in a wetland or meadow area.\"}]",
  "files": [],
  "json": []
}
dosubot[bot] commented 3 months ago

To ensure that your custom tool's JSON array output is parsed correctly as a JSON array instead of being returned as a text string in Dify version 0.6.11, you need to modify the _invoke method to return the result directly as a JSON object rather than converting it to a string. Here is the modified code:

import json
from typing import Any, Union

from jsonpath_ng import parse

from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool

class JSONParseTool(BuiltinTool):
    def _invoke(self,
                user_id: str,
                tool_parameters: dict[str, Any],
                ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
        """
            invoke tools
        """
        # get content
        content = tool_parameters.get('content', '')
        if not content:
            return self.create_text_message('Invalid parameter content')

        # get json filter
        json_filter = tool_parameters.get('json_filter', '')
        if not json_filter:
            return self.create_text_message('Invalid parameter json_filter')

        try:
            result = self._extract(content, json_filter)
            if isinstance(result, str):
                return self.create_text_message(result)
            else:
                return ToolInvokeMessage(result)
        except Exception:
            return self.create_text_message('Failed to extract JSON content')

    # Extract data from JSON content
    def _extract(self, content: str, json_filter: str) -> Any:
        try:
            input_data = json.loads(content)
            expr = parse(json_filter)
            result = [match.value for match in expr.find(input_data)]

            if len(result) == 1:
                result = result[0]

            if isinstance(result, (dict, list)):
                return result
            elif isinstance(result, (str, int, float, bool)) or result is None:
                return str(result)
            else:
                return repr(result)
        except Exception as e:
            return str(e)

In this modification, the _invoke method checks if the result is a string and returns it as a text message. If the result is not a string (i.e., it is a JSON object or array), it returns it directly as a ToolInvokeMessage. This ensures that JSON arrays are parsed correctly and not returned as text strings [1][2].

To continue talking to Dosu, mention @dosu.

crazywoola commented 3 months ago

Link https://github.com/langgenius/dify/issues/6605