Prechádzať zdrojové kódy

Add perplexity search as a new tool (#7861)

Joshua 7 mesiacov pred
rodič
commit
7193e189f3

+ 1 - 0
api/core/tools/provider/_position.yaml

@@ -1,5 +1,6 @@
 - google
 - bing
+- perplexity
 - duckduckgo
 - searchapi
 - serper

+ 3 - 0
api/core/tools/provider/builtin/perplexity/_assets/icon.svg

@@ -0,0 +1,3 @@
+<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M101.008 42L190.99 124.905L190.99 124.886L190.99 42.1913H208.506L208.506 125.276L298.891 42V136.524L336 136.524V272.866H299.005V357.035L208.506 277.525L208.506 357.948H190.99L190.99 278.836L101.11 358V272.866H64V136.524H101.008V42ZM177.785 153.826H81.5159V255.564H101.088V223.472L177.785 153.826ZM118.625 231.149V319.392L190.99 255.655L190.99 165.421L118.625 231.149ZM209.01 254.812V165.336L281.396 231.068V272.866H281.489V318.491L209.01 254.812ZM299.005 255.564H318.484V153.826L222.932 153.826L299.005 222.751V255.564ZM281.375 136.524V81.7983L221.977 136.524L281.375 136.524ZM177.921 136.524H118.524V81.7983L177.921 136.524Z" fill="black"/>
+</svg>

+ 46 - 0
api/core/tools/provider/builtin/perplexity/perplexity.py

@@ -0,0 +1,46 @@
+from typing import Any
+
+import requests
+
+from core.tools.errors import ToolProviderCredentialValidationError
+from core.tools.provider.builtin.perplexity.tools.perplexity_search import PERPLEXITY_API_URL
+from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
+
+
+class PerplexityProvider(BuiltinToolProviderController):
+    def _validate_credentials(self, credentials: dict[str, Any]) -> None:
+        headers = {
+            "Authorization": f"Bearer {credentials.get('perplexity_api_key')}",
+            "Content-Type": "application/json"
+        }
+        
+        payload = {
+            "model": "llama-3.1-sonar-small-128k-online",
+            "messages": [
+                {
+                    "role": "system",
+                    "content": "You are a helpful assistant."
+                },
+                {
+                    "role": "user",
+                    "content": "Hello"
+                }
+            ],
+            "max_tokens": 5,
+            "temperature": 0.1,
+            "top_p": 0.9,
+            "stream": False
+        }
+
+        try:
+            response = requests.post(PERPLEXITY_API_URL, json=payload, headers=headers)
+            response.raise_for_status()
+        except requests.RequestException as e:
+            raise ToolProviderCredentialValidationError(
+                f"Failed to validate Perplexity API key: {str(e)}"
+            )
+
+        if response.status_code != 200:
+            raise ToolProviderCredentialValidationError(
+                f"Perplexity API key is invalid. Status code: {response.status_code}"
+            )

+ 26 - 0
api/core/tools/provider/builtin/perplexity/perplexity.yaml

@@ -0,0 +1,26 @@
+identity:
+  author: Dify
+  name: perplexity
+  label:
+    en_US: Perplexity
+    zh_Hans: Perplexity
+  description:
+    en_US: Perplexity.AI
+    zh_Hans: Perplexity.AI
+  icon: icon.svg
+  tags:
+    - search
+credentials_for_provider:
+  perplexity_api_key:
+    type: secret-input
+    required: true
+    label:
+      en_US: Perplexity API key
+      zh_Hans: Perplexity API key
+    placeholder:
+      en_US: Please input your Perplexity API key
+      zh_Hans: 请输入你的 Perplexity API key
+    help:
+      en_US: Get your Perplexity API key from Perplexity
+      zh_Hans: 从 Perplexity 获取您的 Perplexity API key
+    url: https://www.perplexity.ai/settings/api

+ 72 - 0
api/core/tools/provider/builtin/perplexity/tools/perplexity_search.py

@@ -0,0 +1,72 @@
+import json
+from typing import Any, Union
+
+import requests
+
+from core.tools.entities.tool_entities import ToolInvokeMessage
+from core.tools.tool.builtin_tool import BuiltinTool
+
+PERPLEXITY_API_URL = "https://api.perplexity.ai/chat/completions"
+
+class PerplexityAITool(BuiltinTool):
+    def _parse_response(self, response: dict) -> dict:
+        """Parse the response from Perplexity AI API"""
+        if 'choices' in response and len(response['choices']) > 0:
+            message = response['choices'][0]['message']
+            return {
+                'content': message.get('content', ''),
+                'role': message.get('role', ''),
+                'citations': response.get('citations', [])
+            }
+        else:
+            return {'content': 'Unable to get a valid response', 'role': 'assistant', 'citations': []}
+
+    def _invoke(self,
+                user_id: str,
+                tool_parameters: dict[str, Any],
+                ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
+        headers = {
+            "Authorization": f"Bearer {self.runtime.credentials['perplexity_api_key']}",
+            "Content-Type": "application/json"
+        }
+        
+        payload = {
+            "model": tool_parameters.get('model', 'llama-3.1-sonar-small-128k-online'),
+            "messages": [
+                {
+                    "role": "system",
+                    "content": "Be precise and concise."
+                },
+                {
+                    "role": "user",
+                    "content": tool_parameters['query']
+                }
+            ],
+            "max_tokens": tool_parameters.get('max_tokens', 4096),
+            "temperature": tool_parameters.get('temperature', 0.7),
+            "top_p": tool_parameters.get('top_p', 1),
+            "top_k": tool_parameters.get('top_k', 5),
+            "presence_penalty": tool_parameters.get('presence_penalty', 0),
+            "frequency_penalty": tool_parameters.get('frequency_penalty', 1),
+            "stream": False
+        }
+        
+        if 'search_recency_filter' in tool_parameters:
+            payload['search_recency_filter'] = tool_parameters['search_recency_filter']
+        if 'return_citations' in tool_parameters:
+            payload['return_citations'] = tool_parameters['return_citations']
+        if 'search_domain_filter' in tool_parameters:
+            if isinstance(tool_parameters['search_domain_filter'], str):
+                payload['search_domain_filter'] = [tool_parameters['search_domain_filter']]
+            elif isinstance(tool_parameters['search_domain_filter'], list):
+                payload['search_domain_filter'] = tool_parameters['search_domain_filter']
+        
+
+        response = requests.post(url=PERPLEXITY_API_URL, json=payload, headers=headers)
+        response.raise_for_status()
+        valuable_res = self._parse_response(response.json())
+        
+        return [
+            self.create_json_message(valuable_res),
+            self.create_text_message(json.dumps(valuable_res, ensure_ascii=False, indent=2))
+        ]

+ 178 - 0
api/core/tools/provider/builtin/perplexity/tools/perplexity_search.yaml

@@ -0,0 +1,178 @@
+identity:
+  name: perplexity
+  author: Dify
+  label:
+    en_US: Perplexity Search
+description:
+  human:
+    en_US: Search information using Perplexity AI's language models.
+  llm: This tool is used to search information using Perplexity AI's language models.
+parameters:
+  - name: query
+    type: string
+    required: true
+    label:
+      en_US: Query
+      zh_Hans: 查询
+    human_description:
+      en_US: The text query to be processed by the AI model.
+      zh_Hans: 要由 AI 模型处理的文本查询。
+    form: llm
+  - name: model
+    type: select
+    required: false
+    label:
+      en_US: Model Name
+      zh_Hans: 模型名称
+    human_description:
+      en_US: The Perplexity AI model to use for generating the response.
+      zh_Hans: 用于生成响应的 Perplexity AI 模型。
+    form: form
+    default: "llama-3.1-sonar-small-128k-online"
+    options:
+      - value: llama-3.1-sonar-small-128k-online
+        label:
+          en_US: llama-3.1-sonar-small-128k-online
+          zh_Hans: llama-3.1-sonar-small-128k-online
+      - value: llama-3.1-sonar-large-128k-online
+        label:
+          en_US: llama-3.1-sonar-large-128k-online
+          zh_Hans: llama-3.1-sonar-large-128k-online
+      - value: llama-3.1-sonar-huge-128k-online
+        label:
+          en_US: llama-3.1-sonar-huge-128k-online
+          zh_Hans: llama-3.1-sonar-huge-128k-online
+  - name: max_tokens
+    type: number
+    required: false
+    label:
+      en_US: Max Tokens
+      zh_Hans: 最大令牌数
+      pt_BR: Máximo de Tokens
+    human_description:
+      en_US: The maximum number of tokens to generate in the response.
+      zh_Hans: 在响应中生成的最大令牌数。
+      pt_BR: O número máximo de tokens a serem gerados na resposta.
+    form: form
+    default: 4096
+    min: 1
+    max: 4096
+  - name: temperature
+    type: number
+    required: false
+    label:
+      en_US: Temperature
+      zh_Hans: 温度
+      pt_BR: Temperatura
+    human_description:
+      en_US: Controls randomness in the output. Lower values make the output more focused and deterministic.
+      zh_Hans: 控制输出的随机性。较低的值使输出更加集中和确定。
+    form: form
+    default: 0.7
+    min: 0
+    max: 1
+  - name: top_k
+    type: number
+    required: false
+    label:
+      en_US: Top K
+      zh_Hans: 取样数量
+    human_description:
+      en_US: The number of top results to consider for response generation.
+      zh_Hans: 用于生成响应的顶部结果数量。
+    form: form
+    default: 5
+    min: 1
+    max: 100
+  - name: top_p
+    type: number
+    required: false
+    label:
+      en_US: Top P
+      zh_Hans: Top P
+    human_description:
+      en_US: Controls diversity via nucleus sampling.
+      zh_Hans: 通过核心采样控制多样性。
+    form: form
+    default: 1
+    min: 0.1
+    max: 1
+    step: 0.1
+  - name: presence_penalty
+    type: number
+    required: false
+    label:
+      en_US: Presence Penalty
+      zh_Hans: 存在惩罚
+    human_description:
+      en_US: Positive values penalize new tokens based on whether they appear in the text so far.
+      zh_Hans: 正值会根据新词元是否已经出现在文本中来对其进行惩罚。
+    form: form
+    default: 0
+    min: -1.0
+    max: 1.0
+    step: 0.1
+  - name: frequency_penalty
+    type: number
+    required: false
+    label:
+      en_US: Frequency Penalty
+      zh_Hans: 频率惩罚
+    human_description:
+      en_US: Positive values penalize new tokens based on their existing frequency in the text so far.
+      zh_Hans: 正值会根据新词元在文本中已经出现的频率来对其进行惩罚。
+    form: form
+    default: 1
+    min: 0.1
+    max: 1.0
+    step: 0.1
+  - name: return_citations
+    type: boolean
+    required: false
+    label:
+      en_US: Return Citations
+      zh_Hans: 返回引用
+    human_description:
+      en_US: Whether to return citations in the response.
+      zh_Hans: 是否在响应中返回引用。
+    form: form
+    default: true
+  - name: search_domain_filter
+    type: string
+    required: false
+    label:
+      en_US: Search Domain Filter
+      zh_Hans: 搜索域过滤器
+    human_description:
+      en_US: Domain to filter the search results.
+      zh_Hans: 用于过滤搜索结果的域名。
+    form: form
+    default: ""
+  - name: search_recency_filter
+    type: select
+    required: false
+    label:
+      en_US: Search Recency Filter
+      zh_Hans: 搜索时间过滤器
+    human_description:
+      en_US: Filter for search results based on recency.
+      zh_Hans: 基于时间筛选搜索结果。
+    form: form
+    default: "month"
+    options:
+      - value: day
+        label:
+          en_US: Day
+          zh_Hans: 天
+      - value: week
+        label:
+          en_US: Week
+          zh_Hans: 周
+      - value: month
+        label:
+          en_US: Month
+          zh_Hans: 月
+      - value: year
+        label:
+          en_US: Year
+          zh_Hans: 年