This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
- audio
- code
- time
- webscraper

View File

@@ -0,0 +1,221 @@
from abc import abstractmethod
from os import listdir, path
from typing import Any
from core.entities.provider_entities import ProviderConfig
from core.helper.module_import_helper import load_single_subclass_from_source
from core.plugin.entities.plugin_daemon import CredentialType
from core.tools.__base.tool_provider import ToolProviderController
from core.tools.__base.tool_runtime import ToolRuntime
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import (
OAuthSchema,
ToolEntity,
ToolProviderEntity,
ToolProviderType,
)
from core.tools.entities.values import ToolLabelEnum, default_tool_label_dict
from core.tools.errors import (
ToolProviderNotFoundError,
)
from core.tools.utils.yaml_utils import load_yaml_file_cached
class BuiltinToolProviderController(ToolProviderController):
tools: list[BuiltinTool]
def __init__(self, **data: Any):
self.tools = []
# load provider yaml
provider = self.__class__.__module__.split(".")[-1]
yaml_path = path.join(path.dirname(path.realpath(__file__)), "providers", provider, f"{provider}.yaml")
try:
provider_yaml = load_yaml_file_cached(yaml_path)
except Exception as e:
raise ToolProviderNotFoundError(f"can not load provider yaml for {provider}: {e}")
if "credentials_for_provider" in provider_yaml and provider_yaml["credentials_for_provider"] is not None:
# set credentials name
for credential_name in provider_yaml["credentials_for_provider"]:
provider_yaml["credentials_for_provider"][credential_name]["name"] = credential_name
credentials_schema = []
for credential in provider_yaml.get("credentials_for_provider", {}):
credential_dict = provider_yaml.get("credentials_for_provider", {}).get(credential, {})
credentials_schema.append(credential_dict)
oauth_schema = None
if provider_yaml.get("oauth_schema", None) is not None:
oauth_schema = OAuthSchema(
client_schema=provider_yaml.get("oauth_schema", {}).get("client_schema", []),
credentials_schema=provider_yaml.get("oauth_schema", {}).get("credentials_schema", []),
)
super().__init__(
entity=ToolProviderEntity(
identity=provider_yaml["identity"],
credentials_schema=credentials_schema,
oauth_schema=oauth_schema,
),
)
self._load_tools()
def _load_tools(self):
provider = self.entity.identity.name
tool_path = path.join(path.dirname(path.realpath(__file__)), "providers", provider, "tools")
# get all the yaml files in the tool path
tool_files = list(filter(lambda x: x.endswith(".yaml") and not x.startswith("__"), listdir(tool_path)))
tools = []
for tool_file in tool_files:
# get tool name
tool_name = tool_file.split(".")[0]
tool = load_yaml_file_cached(path.join(tool_path, tool_file))
# get tool class, import the module
assistant_tool_class: type = load_single_subclass_from_source(
module_name=f"core.tools.builtin_tool.providers.{provider}.tools.{tool_name}",
script_path=path.join(
path.dirname(path.realpath(__file__)),
"builtin_tool",
"providers",
provider,
"tools",
f"{tool_name}.py",
),
parent_type=BuiltinTool,
)
tool["identity"]["provider"] = provider
tools.append(
assistant_tool_class(
provider=provider,
entity=ToolEntity.model_validate(tool),
runtime=ToolRuntime(tenant_id=""),
)
)
self.tools = tools
def _get_builtin_tools(self) -> list[BuiltinTool]:
"""
returns a list of tools that the provider can provide
:return: list of tools
"""
return self.tools
def get_credentials_schema(self) -> list[ProviderConfig]:
"""
returns the credentials schema of the provider
:return: the credentials schema
"""
return self.get_credentials_schema_by_type(CredentialType.API_KEY)
def get_credentials_schema_by_type(self, credential_type: str) -> list[ProviderConfig]:
"""
returns the credentials schema of the provider
:param credential_type: the type of the credential
:return: the credentials schema of the provider
"""
if credential_type == CredentialType.OAUTH2.value:
return self.entity.oauth_schema.credentials_schema.copy() if self.entity.oauth_schema else []
if credential_type == CredentialType.API_KEY:
return self.entity.credentials_schema.copy() if self.entity.credentials_schema else []
raise ValueError(f"Invalid credential type: {credential_type}")
def get_oauth_client_schema(self) -> list[ProviderConfig]:
"""
returns the oauth client schema of the provider
:return: the oauth client schema
"""
return self.entity.oauth_schema.client_schema.copy() if self.entity.oauth_schema else []
def get_supported_credential_types(self) -> list[CredentialType]:
"""
returns the credential support type of the provider
"""
types = []
if self.entity.credentials_schema is not None and len(self.entity.credentials_schema) > 0:
types.append(CredentialType.API_KEY)
if self.entity.oauth_schema is not None and len(self.entity.oauth_schema.credentials_schema) > 0:
types.append(CredentialType.OAUTH2)
return types
def get_tools(self) -> list[BuiltinTool]:
"""
returns a list of tools that the provider can provide
:return: list of tools
"""
return self._get_builtin_tools()
def get_tool(self, tool_name: str) -> BuiltinTool | None: # type: ignore
"""
returns the tool that the provider can provide
"""
return next(filter(lambda x: x.entity.identity.name == tool_name, self.get_tools()), None)
@property
def need_credentials(self) -> bool:
"""
returns whether the provider needs credentials
:return: whether the provider needs credentials
"""
return (
self.entity.credentials_schema is not None
and len(self.entity.credentials_schema) != 0
or (self.entity.oauth_schema is not None and len(self.entity.oauth_schema.credentials_schema) != 0)
)
@property
def provider_type(self) -> ToolProviderType:
"""
returns the type of the provider
:return: type of the provider
"""
return ToolProviderType.BUILT_IN
@property
def tool_labels(self) -> list[str]:
"""
returns the labels of the provider
:return: labels of the provider
"""
label_enums = self._get_tool_labels()
return [default_tool_label_dict[label].name for label in label_enums]
def _get_tool_labels(self) -> list[ToolLabelEnum]:
"""
returns the labels of the provider
"""
return self.entity.identity.tags or []
def validate_credentials(self, user_id: str, credentials: dict[str, Any]):
"""
validate the credentials of the provider
:param user_id: use id
:param credentials: the credentials of the tool
"""
# validate credentials format
self.validate_credentials_format(credentials)
# validate credentials
self._validate_credentials(user_id, credentials)
@abstractmethod
def _validate_credentials(self, user_id: str, credentials: dict[str, Any]):
"""
validate the credentials of the provider
:param user_id: use id
:param credentials: the credentials of the tool
"""
pass

View File

@@ -0,0 +1,20 @@
import os.path
from core.helper.position_helper import get_tool_position_map, sort_by_position_map
from core.tools.entities.api_entities import ToolProviderApiEntity
class BuiltinToolProviderSort:
_position: dict[str, int] = {}
@classmethod
def sort(cls, providers: list[ToolProviderApiEntity]) -> list[ToolProviderApiEntity]:
if not cls._position:
cls._position = get_tool_position_map(os.path.join(os.path.dirname(__file__), ".."))
def name_func(provider: ToolProviderApiEntity) -> str:
return provider.name
sorted_providers = sort_by_position_map(cls._position, providers, name_func)
return sorted_providers

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200" fill="none">
<path d="M167.358 102.395C167.358 117.174 157.246 129.18 144.61 131.027H137.861C125.225 129.18 115.113 117.174 115.113 102.395H100.792C100.792 123.637 115.118 142.106 133.653 145.801V164.276H147.139V145.801C165.674 142.106 180 124.558 180 102.4H167.358V102.395ZM154.717 62.677C154.717 53.4397 147.979 46.9765 140.396 46.9765C138.523 46.9446 136.663 47.3273 134.924 48.1024C133.185 48.8775 131.603 50.0294 130.27 51.4909C128.936 52.9524 127.878 54.6943 127.157 56.6148C126.436 58.5354 126.066 60.5962 126.07 62.677V78.3775H154.717V70.4478V62.677ZM126.07 102.395C126.07 111.632 132.813 118.095 140.396 118.095C142.269 118.127 144.13 117.744 145.868 116.969C147.607 116.194 149.189 115.042 150.523 113.581C151.856 112.119 152.914 110.377 153.635 108.457C154.356 106.536 154.726 104.475 154.722 102.395V86.694H126.07V102.395ZM92.1297 45.8938L70.4796 21.7595L69.4235 20.5865L59.604 20L68.3674 20.5865L67.3113 21.7654L64.1429 25.2961L63.6149 25.8826L64.1429 27.0614L66.2552 29.4133L77.8723 42.3631H54.1099C35.1 43.5361 20.3146 61.1896 20.3146 81.7874V83.5527H28.2354V81.7932C28.2354 65.8992 39.8525 52.3628 54.1099 51.1899H77.8723L66.2552 64.1338L64.671 65.8992L64.1429 67.0722L63.6149 67.6645L64.1429 68.251L68.3674 72.9606L68.8954 73.5471L69.4235 72.9606L74.1759 67.6645L92.1297 47.6591L92.6578 47.0727L92.1297 45.8938ZM20 95.8496V118.213H30.033V107.034H50.099V168.821H40.066V180H70.165V168.821H60.132V107.034H80.198V118.213H90.231V95.8496H20Z" fill="#FF0099"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,8 @@
from typing import Any
from core.tools.builtin_tool.provider import BuiltinToolProviderController
class AudioToolProvider(BuiltinToolProviderController):
def _validate_credentials(self, user_id: str, credentials: dict[str, Any]):
pass

View File

@@ -0,0 +1,11 @@
identity:
author: hjlarry
name: audio
label:
en_US: Audio
description:
en_US: A tool for tts and asr.
zh_Hans: 一个用于文本转语音和语音转文本的工具。
icon: icon.svg
tags:
- utilities

View File

@@ -0,0 +1,84 @@
import io
from collections.abc import Generator
from typing import Any
from core.file.enums import FileType
from core.file.file_manager import download
from core.model_manager import ModelManager
from core.model_runtime.entities.model_entities import ModelType
from core.plugin.entities.parameters import PluginParameterOption
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
from services.model_provider_service import ModelProviderService
class ASRTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
file = tool_parameters.get("audio_file")
if file.type != FileType.AUDIO: # type: ignore
yield self.create_text_message("not a valid audio file")
return
audio_binary = io.BytesIO(download(file)) # type: ignore
audio_binary.name = "temp.mp3"
provider, model = tool_parameters.get("model").split("#") # type: ignore
model_manager = ModelManager()
model_instance = model_manager.get_model_instance(
tenant_id=self.runtime.tenant_id,
provider=provider,
model_type=ModelType.SPEECH2TEXT,
model=model,
)
text = model_instance.invoke_speech2text(
file=audio_binary,
user=user_id,
)
yield self.create_text_message(text)
def get_available_models(self) -> list[tuple[str, str]]:
model_provider_service = ModelProviderService()
models = model_provider_service.get_models_by_model_type(
tenant_id=self.runtime.tenant_id, model_type="speech2text"
)
items = []
for provider_model in models:
provider = provider_model.provider
for model in provider_model.models:
items.append((provider, model.model))
return items
def get_runtime_parameters(
self,
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> list[ToolParameter]:
parameters = []
options = []
for provider, model in self.get_available_models():
option = PluginParameterOption(value=f"{provider}#{model}", label=I18nObject(en_US=f"{model}({provider})"))
options.append(option)
parameters.append(
ToolParameter(
name="model",
label=I18nObject(en_US="Model", zh_Hans="Model"),
human_description=I18nObject(
en_US="All available ASR models. You can config model in the Model Provider of Settings.",
zh_Hans="所有可用的 ASR 模型。你可以在设置中的模型供应商里配置。",
),
type=ToolParameter.ToolParameterType.SELECT,
form=ToolParameter.ToolParameterForm.FORM,
required=True,
options=options,
)
)
return parameters

View File

@@ -0,0 +1,22 @@
identity:
name: asr
author: hjlarry
label:
en_US: Speech To Text
description:
human:
en_US: Convert audio file to text.
zh_Hans: 将音频文件转换为文本。
llm: Convert audio file to text.
parameters:
- name: audio_file
type: file
required: true
label:
en_US: Audio File
zh_Hans: 音频文件
human_description:
en_US: The audio file to be converted.
zh_Hans: 要转换的音频文件。
llm_description: The audio file to be converted.
form: llm

View File

@@ -0,0 +1,116 @@
import io
from collections.abc import Generator
from typing import Any
from core.model_manager import ModelManager
from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
from core.plugin.entities.parameters import PluginParameterOption
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
from services.model_provider_service import ModelProviderService
class TTSTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
provider, model = tool_parameters.get("model").split("#") # type: ignore
voice = tool_parameters.get(f"voice#{provider}#{model}")
model_manager = ModelManager()
if not self.runtime:
raise ValueError("Runtime is required")
model_instance = model_manager.get_model_instance(
tenant_id=self.runtime.tenant_id or "",
provider=provider,
model_type=ModelType.TTS,
model=model,
)
if not voice:
voices = model_instance.get_tts_voices()
if voices:
voice = voices[0].get("value")
if not voice:
raise ValueError("Sorry, no voice available.")
else:
raise ValueError("Sorry, no voice available.")
tts = model_instance.invoke_tts(
content_text=tool_parameters.get("text"), # type: ignore
user=user_id,
tenant_id=self.runtime.tenant_id,
voice=voice,
)
buffer = io.BytesIO()
for chunk in tts:
buffer.write(chunk)
wav_bytes = buffer.getvalue()
yield self.create_text_message("Audio generated successfully")
yield self.create_blob_message(
blob=wav_bytes,
meta={"mime_type": "audio/x-wav"},
)
def get_available_models(self) -> list[tuple[str, str, list[Any]]]:
if not self.runtime:
raise ValueError("Runtime is required")
model_provider_service = ModelProviderService()
tid: str = self.runtime.tenant_id or ""
models = model_provider_service.get_models_by_model_type(tenant_id=tid, model_type="tts")
items = []
for provider_model in models:
provider = provider_model.provider
for model in provider_model.models:
voices = model.model_properties.get(ModelPropertyKey.VOICES, [])
items.append((provider, model.model, voices))
return items
def get_runtime_parameters(
self,
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> list[ToolParameter]:
parameters = []
options = []
for provider, model, voices in self.get_available_models():
option = PluginParameterOption(value=f"{provider}#{model}", label=I18nObject(en_US=f"{model}({provider})"))
options.append(option)
parameters.append(
ToolParameter(
name=f"voice#{provider}#{model}",
label=I18nObject(en_US=f"Voice of {model}({provider})"),
human_description=I18nObject(en_US=f"Select a voice for {model} model"),
placeholder=I18nObject(en_US="Select a voice"),
type=ToolParameter.ToolParameterType.SELECT,
form=ToolParameter.ToolParameterForm.FORM,
options=[
PluginParameterOption(value=voice.get("mode"), label=I18nObject(en_US=voice.get("name")))
for voice in voices
],
)
)
parameters.insert(
0,
ToolParameter(
name="model",
label=I18nObject(en_US="Model", zh_Hans="Model"),
human_description=I18nObject(
en_US="All available TTS models. You can config model in the Model Provider of Settings.",
zh_Hans="所有可用的 TTS 模型。你可以在设置中的模型供应商里配置。",
),
type=ToolParameter.ToolParameterType.SELECT,
form=ToolParameter.ToolParameterForm.FORM,
required=True,
placeholder=I18nObject(en_US="Select a model", zh_Hans="选择模型"),
options=options,
),
)
return parameters

View File

@@ -0,0 +1,22 @@
identity:
name: tts
author: hjlarry
label:
en_US: Text To Speech
description:
human:
en_US: Convert text to audio file.
zh_Hans: 将文本转换为音频文件。
llm: Convert text to audio file.
parameters:
- name: text
type: string
required: true
label:
en_US: Text
zh_Hans: 文本
human_description:
en_US: The text to be converted.
zh_Hans: 要转换的文本。
llm_description: The text to be converted.
form: llm

View File

@@ -0,0 +1 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" data-icon="Code" aria-hidden="true"><g id="icons/code"><path id="Vector (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M8.32593 1.69675C8.67754 1.78466 8.89132 2.14096 8.80342 2.49257L6.47009 11.8259C6.38218 12.1775 6.02588 12.3913 5.67427 12.3034C5.32265 12.2155 5.10887 11.8592 5.19678 11.5076L7.53011 2.17424C7.61801 1.82263 7.97431 1.60885 8.32593 1.69675ZM3.96414 4.20273C4.22042 4.45901 4.22042 4.87453 3.96413 5.13081L2.45578 6.63914C2.45577 6.63915 2.45578 6.63914 2.45578 6.63914C2.25645 6.83851 2.25643 7.16168 2.45575 7.36103C2.45574 7.36103 2.45576 7.36104 2.45575 7.36103L3.96413 8.86936C4.22041 9.12564 4.22042 9.54115 3.96414 9.79744C3.70787 10.0537 3.29235 10.0537 3.03607 9.79745L1.52769 8.28913C0.815811 7.57721 0.815803 6.42302 1.52766 5.7111L3.03606 4.20272C3.29234 3.94644 3.70786 3.94644 3.96414 4.20273ZM10.0361 4.20273C10.2923 3.94644 10.7078 3.94644 10.9641 4.20272L12.4725 5.71108C13.1843 6.423 13.1844 7.57717 12.4725 8.28909L10.9641 9.79745C10.7078 10.0537 10.2923 10.0537 10.036 9.79744C9.77977 9.54115 9.77978 9.12564 10.0361 8.86936L11.5444 7.36107C11.7437 7.16172 11.7438 6.83854 11.5444 6.63917C11.5444 6.63915 11.5445 6.63918 11.5444 6.63917L10.0361 5.13081C9.77978 4.87453 9.77978 4.45901 10.0361 4.20273Z" fill="#2e90fa"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,8 @@
from typing import Any
from core.tools.builtin_tool.provider import BuiltinToolProviderController
class CodeToolProvider(BuiltinToolProviderController):
def _validate_credentials(self, user_id: str, credentials: dict[str, Any]):
pass

View File

@@ -0,0 +1,14 @@
identity:
author: Dify
name: code
label:
en_US: Code Interpreter
zh_Hans: 代码解释器
pt_BR: Interpretador de Código
description:
en_US: Run a piece of code and get the result back.
zh_Hans: 运行一段代码并返回结果。
pt_BR: Execute um trecho de código e obtenha o resultado de volta.
icon: icon.svg
tags:
- productivity

View File

@@ -0,0 +1,33 @@
from collections.abc import Generator
from typing import Any
from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.errors import ToolInvokeError
class SimpleCode(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
invoke simple code
"""
language = tool_parameters.get("language", CodeLanguage.PYTHON3)
code = tool_parameters.get("code", "")
if language not in {CodeLanguage.PYTHON3, CodeLanguage.JAVASCRIPT}:
raise ValueError(f"Only python3 and javascript are supported, not {language}")
try:
result = CodeExecutor.execute_code(language, "", code)
yield self.create_text_message(result)
except Exception as e:
raise ToolInvokeError(str(e))

View File

@@ -0,0 +1,51 @@
identity:
name: simple_code
author: Dify
label:
en_US: Code Interpreter
zh_Hans: 代码解释器
pt_BR: Interpretador de Código
description:
human:
en_US: Run code and get the result back. When you're using a lower quality model, please make sure there are some tips help LLM to understand how to write the code.
zh_Hans: 运行一段代码并返回结果。当您使用较低质量的模型时,请确保有一些提示帮助 LLM 理解如何编写代码。
pt_BR: Execute um trecho de código e obtenha o resultado de volta. quando você estiver usando um modelo de qualidade inferior, certifique-se de que existam algumas dicas para ajudar o LLM a entender como escrever o código.
llm: A tool for running code and getting the result back. Only native packages are allowed, network/IO operations are disabled. and you must use print() or console.log() to output the result or result will be empty.
parameters:
- name: language
type: string
required: true
label:
en_US: Language
zh_Hans: 语言
pt_BR: Idioma
human_description:
en_US: The programming language of the code
zh_Hans: 代码的编程语言
pt_BR: A linguagem de programação do código
llm_description: language of the code, only "python3" and "javascript" are supported
form: llm
options:
- value: python3
label:
en_US: Python3
zh_Hans: Python3
pt_BR: Python3
- value: javascript
label:
en_US: JavaScript
zh_Hans: JavaScript
pt_BR: JavaScript
- name: code
type: string
required: true
label:
en_US: Code
zh_Hans: 代码
pt_BR: Código
human_description:
en_US: The code to be executed
zh_Hans: 要执行的代码
pt_BR: O código a ser executado
llm_description: code to be executed, only native packages are allowed, network/IO operations are disabled.
form: llm

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666992 8.00008C0.666992 3.94999 3.95024 0.666748 8.00033 0.666748C12.0504 0.666748 15.3337 3.94999 15.3337 8.00008C15.3337 12.0502 12.0504 15.3334 8.00033 15.3334C3.95024 15.3334 0.666992 12.0502 0.666992 8.00008ZM8.66699 4.00008C8.66699 3.63189 8.36852 3.33341 8.00033 3.33341C7.63213 3.33341 7.33366 3.63189 7.33366 4.00008V8.00008C7.33366 8.2526 7.47633 8.48344 7.70218 8.59637L10.3688 9.9297C10.6982 10.0944 11.0986 9.96088 11.2633 9.63156C11.4279 9.30224 11.2945 8.90179 10.9651 8.73713L8.66699 7.58806V4.00008Z" fill="#EC4A0A"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@@ -0,0 +1,8 @@
from typing import Any
from core.tools.builtin_tool.provider import BuiltinToolProviderController
class WikiPediaProvider(BuiltinToolProviderController):
def _validate_credentials(self, user_id: str, credentials: dict[str, Any]):
pass

View File

@@ -0,0 +1,14 @@
identity:
author: Dify
name: time
label:
en_US: CurrentTime
zh_Hans: 时间
pt_BR: CurrentTime
description:
en_US: A tool for getting the current time.
zh_Hans: 一个用于获取当前时间的工具。
pt_BR: A tool for getting the current time.
icon: icon.svg
tags:
- utilities

View File

@@ -0,0 +1,35 @@
from collections.abc import Generator
from datetime import UTC, datetime
from typing import Any
from pytz import timezone as pytz_timezone
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
class CurrentTimeTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
invoke tools
"""
# get timezone
tz = tool_parameters.get("timezone", "UTC")
fm = tool_parameters.get("format") or "%Y-%m-%d %H:%M:%S %Z"
if tz == "UTC":
yield self.create_text_message(f"{datetime.now(UTC).strftime(fm)}")
return
try:
tz = pytz_timezone(tz)
except Exception:
yield self.create_text_message(f"Invalid timezone: {tz}")
return
yield self.create_text_message(f"{datetime.now(tz).strftime(fm)}")

View File

@@ -0,0 +1,131 @@
identity:
name: current_time
author: Dify
label:
en_US: Current Time
zh_Hans: 获取当前时间
pt_BR: Current Time
description:
human:
en_US: A tool for getting the current time.
zh_Hans: 一个用于获取当前时间的工具。
pt_BR: A tool for getting the current time.
llm: A tool for getting the current time.
parameters:
- name: format
type: string
required: false
label:
en_US: Format
zh_Hans: 格式
pt_BR: Format
human_description:
en_US: Time format in strftime standard.
zh_Hans: strftime 标准的时间格式。
pt_BR: Time format in strftime standard.
form: form
default: "%Y-%m-%d %H:%M:%S"
- name: timezone
type: select
required: false
label:
en_US: Timezone
zh_Hans: 时区
pt_BR: Timezone
human_description:
en_US: Timezone
zh_Hans: 时区
pt_BR: Timezone
form: form
default: UTC
options:
- value: UTC
label:
en_US: UTC
zh_Hans: UTC
pt_BR: UTC
- value: America/New_York
label:
en_US: America/New_York
zh_Hans: 美洲/纽约
pt_BR: America/New_York
- value: America/Los_Angeles
label:
en_US: America/Los_Angeles
zh_Hans: 美洲/洛杉矶
pt_BR: America/Los_Angeles
- value: America/Chicago
label:
en_US: America/Chicago
zh_Hans: 美洲/芝加哥
pt_BR: America/Chicago
- value: America/Sao_Paulo
label:
en_US: America/Sao_Paulo
zh_Hans: 美洲/圣保罗
pt_BR: América/São Paulo
- value: Asia/Shanghai
label:
en_US: Asia/Shanghai
zh_Hans: 亚洲/上海
pt_BR: Asia/Shanghai
- value: Asia/Ho_Chi_Minh
label:
en_US: Asia/Ho_Chi_Minh
zh_Hans: 亚洲/胡志明市
pt_BR: Ásia/Ho Chi Minh
- value: Asia/Tokyo
label:
en_US: Asia/Tokyo
zh_Hans: 亚洲/东京
pt_BR: Asia/Tokyo
- value: Asia/Dubai
label:
en_US: Asia/Dubai
zh_Hans: 亚洲/迪拜
pt_BR: Asia/Dubai
- value: Asia/Kolkata
label:
en_US: Asia/Kolkata
zh_Hans: 亚洲/加尔各答
pt_BR: Asia/Kolkata
- value: Asia/Seoul
label:
en_US: Asia/Seoul
zh_Hans: 亚洲/首尔
pt_BR: Asia/Seoul
- value: Asia/Singapore
label:
en_US: Asia/Singapore
zh_Hans: 亚洲/新加坡
pt_BR: Asia/Singapore
- value: Europe/London
label:
en_US: Europe/London
zh_Hans: 欧洲/伦敦
pt_BR: Europe/London
- value: Europe/Berlin
label:
en_US: Europe/Berlin
zh_Hans: 欧洲/柏林
pt_BR: Europe/Berlin
- value: Europe/Moscow
label:
en_US: Europe/Moscow
zh_Hans: 欧洲/莫斯科
pt_BR: Europe/Moscow
- value: Australia/Sydney
label:
en_US: Australia/Sydney
zh_Hans: 澳大利亚/悉尼
pt_BR: Australia/Sydney
- value: Pacific/Auckland
label:
en_US: Pacific/Auckland
zh_Hans: 太平洋/奥克兰
pt_BR: Pacific/Auckland
- value: Africa/Cairo
label:
en_US: Africa/Cairo
zh_Hans: 非洲/开罗
pt_BR: Africa/Cairo

View File

@@ -0,0 +1,50 @@
from collections.abc import Generator
from datetime import datetime
from typing import Any
import pytz
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.errors import ToolInvokeError
class LocaltimeToTimestampTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
Convert localtime to timestamp
"""
localtime = tool_parameters.get("localtime")
timezone = tool_parameters.get("timezone", "Asia/Shanghai")
if not timezone:
timezone = None
time_format = "%Y-%m-%d %H:%M:%S"
timestamp = self.localtime_to_timestamp(localtime, time_format, timezone) # type: ignore
if not timestamp:
yield self.create_text_message(f"Invalid localtime: {localtime}")
return
yield self.create_text_message(f"{timestamp}")
# TODO: this method's type is messy
@staticmethod
def localtime_to_timestamp(localtime: str, time_format: str, local_tz=None) -> int | None:
try:
local_time = datetime.strptime(localtime, time_format)
if local_tz is None:
localtime = local_time.astimezone() # type: ignore
elif isinstance(local_tz, str):
local_tz = pytz.timezone(local_tz)
localtime = local_tz.localize(local_time) # type: ignore
timestamp = int(localtime.timestamp()) # type: ignore
return timestamp
except Exception as e:
raise ToolInvokeError(str(e))

View File

@@ -0,0 +1,33 @@
identity:
name: localtime_to_timestamp
author: zhuhao
label:
en_US: localtime to timestamp
zh_Hans: 获取时间戳
description:
human:
en_US: A tool for localtime convert to timestamp
zh_Hans: 获取时间戳
llm: A tool for localtime convert to timestamp
parameters:
- name: localtime
type: string
required: true
form: llm
label:
en_US: localtime
zh_Hans: 本地时间
human_description:
en_US: localtime, such as 2024-1-1 0:0:0
zh_Hans: 本地时间,比如 2024-1-1 0:0:0
- name: timezone
type: string
required: false
form: llm
label:
en_US: Timezone
zh_Hans: 时区
human_description:
en_US: Timezone, such as Asia/Shanghai
zh_Hans: 时区,比如 Asia/Shanghai
default: Asia/Shanghai

View File

@@ -0,0 +1,49 @@
from collections.abc import Generator
from datetime import datetime
from typing import Any
import pytz
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.errors import ToolInvokeError
class TimestampToLocaltimeTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
Convert timestamp to localtime
"""
timestamp: int = tool_parameters.get("timestamp", 0)
timezone = tool_parameters.get("timezone", "Asia/Shanghai")
if not timezone:
timezone = None
time_format = "%Y-%m-%d %H:%M:%S"
locatime = self.timestamp_to_localtime(timestamp, timezone)
if not locatime:
yield self.create_text_message(f"Invalid timestamp: {timestamp}")
return
localtime_format = locatime.strftime(time_format)
yield self.create_text_message(f"{localtime_format}")
@staticmethod
def timestamp_to_localtime(timestamp: int, local_tz=None) -> datetime | None:
try:
if local_tz is None:
local_tz = datetime.now().astimezone().tzinfo
if isinstance(local_tz, str):
local_tz = pytz.timezone(local_tz)
local_time = datetime.fromtimestamp(timestamp, local_tz)
return local_time
except Exception as e:
raise ToolInvokeError(str(e))

View File

@@ -0,0 +1,33 @@
identity:
name: timestamp_to_localtime
author: zhuhao
label:
en_US: Timestamp to localtime
zh_Hans: 时间戳转换
description:
human:
en_US: A tool for timestamp convert to localtime
zh_Hans: 时间戳转换
llm: A tool for timestamp convert to localtime
parameters:
- name: timestamp
type: number
required: true
form: llm
label:
en_US: Timestamp
zh_Hans: 时间戳
human_description:
en_US: Timestamp
zh_Hans: 时间戳
- name: timezone
type: string
required: false
form: llm
label:
en_US: Timezone
zh_Hans: 时区
human_description:
en_US: Timezone, such as Asia/Shanghai
zh_Hans: 时区,比如 Asia/Shanghai
default: Asia/Shanghai

View File

@@ -0,0 +1,53 @@
from collections.abc import Generator
from datetime import datetime
from typing import Any
import pytz
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.errors import ToolInvokeError
class TimezoneConversionTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
Convert time to equivalent time zone
"""
current_time = tool_parameters.get("current_time")
current_timezone = tool_parameters.get("current_timezone", "Asia/Shanghai")
target_timezone = tool_parameters.get("target_timezone", "Asia/Tokyo")
target_time = self.timezone_convert(current_time, current_timezone, target_timezone) # type: ignore
if not target_time:
yield self.create_text_message(
f"Invalid datetime and timezone: {current_time},{current_timezone},{target_timezone}"
)
return
yield self.create_text_message(f"{target_time}")
@staticmethod
def timezone_convert(current_time: str, source_timezone: str, target_timezone: str) -> str:
"""
Convert a time string from source timezone to target timezone.
"""
time_format = "%Y-%m-%d %H:%M:%S"
try:
# get source timezone
input_timezone = pytz.timezone(source_timezone)
# get target timezone
output_timezone = pytz.timezone(target_timezone)
local_time = datetime.strptime(current_time, time_format)
datetime_with_tz = input_timezone.localize(local_time)
# timezone convert
converted_datetime = datetime_with_tz.astimezone(output_timezone)
return converted_datetime.strftime(time_format)
except Exception as e:
raise ToolInvokeError(str(e))

View File

@@ -0,0 +1,44 @@
identity:
name: timezone_conversion
author: zhuhao
label:
en_US: convert time to equivalent time zone
zh_Hans: 时区转换
description:
human:
en_US: A tool to convert time to equivalent time zone
zh_Hans: 时区转换
llm: A tool to convert time to equivalent time zone
parameters:
- name: current_time
type: string
required: true
form: llm
label:
en_US: current time
zh_Hans: 当前时间
human_description:
en_US: current time, such as 2024-1-1 0:0:0
zh_Hans: 当前时间,比如 2024-1-1 0:0:0
- name: current_timezone
type: string
required: true
form: llm
label:
en_US: Current Timezone
zh_Hans: 当前时区
human_description:
en_US: Current Timezone, such as Asia/Shanghai
zh_Hans: 当前时区,比如 Asia/Shanghai
default: Asia/Shanghai
- name: target_timezone
type: string
required: true
form: llm
label:
en_US: Target Timezone
zh_Hans: 目标时区
human_description:
en_US: Target Timezone, such as Asia/Tokyo
zh_Hans: 目标时区,比如 Asia/Tokyo
default: Asia/Tokyo

View File

@@ -0,0 +1,50 @@
import calendar
from collections.abc import Generator
from datetime import datetime
from typing import Any
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
class WeekdayTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
Calculate the day of the week for a given date
"""
year = tool_parameters.get("year")
month = tool_parameters.get("month")
if month is None:
raise ValueError("Month is required")
day = tool_parameters.get("day")
date_obj = self.convert_datetime(year, month, day)
if not date_obj:
yield self.create_text_message(f"Invalid date: Year {year}, Month {month}, Day {day}.")
return
weekday_name = calendar.day_name[date_obj.weekday()]
month_name = calendar.month_name[month]
readable_date = f"{month_name} {date_obj.day}, {date_obj.year}"
yield self.create_text_message(f"{readable_date} is {weekday_name}.")
@staticmethod
def convert_datetime(year, month, day) -> datetime | None:
try:
# allowed range in datetime module
if not (year >= 1 and 1 <= month <= 12 and 1 <= day <= 31):
return None
year = int(year)
month = int(month)
day = int(day)
return datetime(year, month, day)
except ValueError:
return None

View File

@@ -0,0 +1,42 @@
identity:
name: weekday
author: Bowen Liang
label:
en_US: Weekday Calculator
zh_Hans: 星期几计算器
description:
human:
en_US: A tool for calculating the weekday of a given date.
zh_Hans: 计算指定日期为星期几的工具。
llm: A tool for calculating the weekday of a given date by year, month and day.
parameters:
- name: year
type: number
required: true
form: llm
label:
en_US: Year
zh_Hans:
human_description:
en_US: Year
zh_Hans:
- name: month
type: number
required: true
form: llm
label:
en_US: Month
zh_Hans:
human_description:
en_US: Month
zh_Hans:
- name: day
type: number
required: true
form: llm
label:
en_US: day
zh_Hans:
human_description:
en_US: day
zh_Hans:

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" viewBox="0 0 16 17" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.6665 1.16667C1.56193 1.16667 0.666504 2.0621 0.666504 3.16667C0.666504 4.27124 1.56193 5.16667 2.6665 5.16667C2.79161 5.16667 2.91403 5.15519 3.03277 5.13321C2.3808 6.09319 1.99984 7.25211 1.99984 8.5C1.99984 9.7479 2.3808 10.9068 3.03277 11.8668C2.91403 11.8448 2.79161 11.8333 2.6665 11.8333C1.56193 11.8333 0.666504 12.7288 0.666504 13.8333C0.666504 14.9379 1.56193 15.8333 2.6665 15.8333C3.77107 15.8333 4.6665 14.9379 4.6665 13.8333C4.6665 13.7082 4.65502 13.5858 4.63304 13.4671C5.59302 14.119 6.75194 14.5 7.99984 14.5C9.24773 14.5 10.4066 14.119 11.3666 13.4671C11.3447 13.5858 11.3332 13.7082 11.3332 13.8333C11.3332 14.9379 12.2286 15.8333 13.3332 15.8333C14.4377 15.8333 15.3332 14.9379 15.3332 13.8333C15.3332 12.7288 14.4377 11.8333 13.3332 11.8333C13.2081 11.8333 13.0856 11.8448 12.9669 11.8668C13.6189 10.9068 13.9998 9.7479 13.9998 8.5C13.9998 7.25211 13.6189 6.09319 12.9669 5.13321C13.0856 5.15519 13.2081 5.16667 13.3332 5.16667C14.4377 5.16667 15.3332 4.27124 15.3332 3.16667C15.3332 2.0621 14.4377 1.16667 13.3332 1.16667C12.2286 1.16667 11.3332 2.0621 11.3332 3.16667C11.3332 3.29177 11.3447 3.41419 11.3666 3.53293C10.4066 2.88097 9.24773 2.50001 7.99984 2.50001C6.75194 2.50001 5.59302 2.88097 4.63304 3.53293C4.65502 3.41419 4.6665 3.29177 4.6665 3.16667C4.6665 2.0621 3.77107 1.16667 2.6665 1.16667ZM3.38043 7.83334C3.63081 6.08287 4.85262 4.64578 6.48223 4.08565C5.79223 5.22099 5.36488 6.50185 5.23815 7.83334H3.38043ZM6.48228 12.9144C4.85264 12.3543 3.63082 10.9172 3.38043 9.16667H5.23815C5.3649 10.4982 5.79226 11.779 6.48228 12.9144ZM12.6192 9.16667C12.3689 10.9168 11.1475 12.3537 9.5183 12.9141C10.2082 11.7788 10.6355 10.498 10.7622 9.16667H12.6192ZM9.51834 4.08596C11.1475 4.64631 12.3689 6.0832 12.6192 7.83334H10.7622C10.6355 6.50197 10.2082 5.22123 9.51834 4.08596ZM9.4218 7.83334C9.27457 6.52262 8.78381 5.27411 8.00019 4.2145C7.21658 5.27411 6.72582 6.52262 6.57859 7.83334H9.4218ZM6.5786 9.16667C6.72583 10.4774 7.21659 11.7259 8.00019 12.7855C8.7838 11.7259 9.27456 10.4774 9.42179 9.16667H6.5786Z" fill="#DD2590"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,39 @@
from collections.abc import Generator
from typing import Any
from core.tools.builtin_tool.tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.errors import ToolInvokeError
from core.tools.utils.web_reader_tool import get_url
class WebscraperTool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
conversation_id: str | None = None,
app_id: str | None = None,
message_id: str | None = None,
) -> Generator[ToolInvokeMessage, None, None]:
"""
invoke tools
"""
try:
url = tool_parameters.get("url", "")
user_agent = tool_parameters.get("user_agent", "")
if not url:
yield self.create_text_message("Please input url")
return
# get webpage
result = get_url(url, user_agent=user_agent)
if tool_parameters.get("generate_summary"):
# summarize and return
yield self.create_text_message(self.summary(user_id=user_id, content=result))
else:
# return full webpage
yield self.create_text_message(result)
except Exception as e:
raise ToolInvokeError(str(e))

View File

@@ -0,0 +1,60 @@
identity:
name: webscraper
author: Dify
label:
en_US: Web Scraper
zh_Hans: 网页爬虫
pt_BR: Web Scraper
description:
human:
en_US: A tool for scraping webpages.
zh_Hans: 一个用于爬取网页的工具。
pt_BR: A tool for scraping webpages.
llm: A tool for scraping webpages. Input should be a URL.
parameters:
- name: url
type: string
required: true
label:
en_US: URL
zh_Hans: 网页链接
pt_BR: URL
human_description:
en_US: used for linking to webpages
zh_Hans: 用于链接到网页
pt_BR: used for linking to webpages
llm_description: url for scraping
form: llm
- name: user_agent
type: string
required: false
label:
en_US: User Agent
zh_Hans: User Agent
pt_BR: User Agent
human_description:
en_US: used for identifying the browser.
zh_Hans: 用于识别浏览器。
pt_BR: used for identifying the browser.
form: form
default: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.1000.0 Safari/537.36
- name: generate_summary
type: boolean
required: false
label:
en_US: Whether to generate summary
zh_Hans: 是否生成摘要
human_description:
en_US: If true, the crawler will only return the page summary content.
zh_Hans: 如果启用,爬虫将仅返回页面摘要内容。
form: form
options:
- value: "true"
label:
en_US: "Yes"
zh_Hans:
- value: "false"
label:
en_US: "No"
zh_Hans:
default: "false"

View File

@@ -0,0 +1,11 @@
from typing import Any
from core.tools.builtin_tool.provider import BuiltinToolProviderController
class WebscraperProvider(BuiltinToolProviderController):
def _validate_credentials(self, user_id: str, credentials: dict[str, Any]):
"""
Validate credentials
"""
pass

View File

@@ -0,0 +1,15 @@
identity:
author: Dify
name: webscraper
label:
en_US: WebScraper
zh_Hans: 网页抓取
pt_BR: WebScraper
description:
en_US: Web Scrapper tool kit is used to scrape web
zh_Hans: 一个用于抓取网页的工具。
pt_BR: Web Scrapper tool kit is used to scrape web
icon: icon.svg
tags:
- productivity
credentials_for_provider: {}

View File

@@ -0,0 +1,148 @@
from core.model_runtime.entities.llm_entities import LLMResult
from core.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage
from core.tools.__base.tool import Tool
from core.tools.__base.tool_runtime import ToolRuntime
from core.tools.entities.tool_entities import ToolProviderType
from core.tools.utils.model_invocation_utils import ModelInvocationUtils
_SUMMARY_PROMPT = """You are a professional language researcher, you are interested in the language
and you can quickly aimed at the main point of an webpage and reproduce it in your own words but
retain the original meaning and keep the key points.
however, the text you got is too long, what you got is possible a part of the text.
Please summarize the text you got.
"""
class BuiltinTool(Tool):
"""
Builtin tool
:param meta: the meta data of a tool call processing
"""
def __init__(self, provider: str, **kwargs):
super().__init__(**kwargs)
self.provider = provider
def fork_tool_runtime(self, runtime: ToolRuntime) -> "BuiltinTool":
"""
fork a new tool with metadata
:return: the new tool
"""
return self.__class__(
entity=self.entity.model_copy(),
runtime=runtime,
provider=self.provider,
)
def invoke_model(self, user_id: str, prompt_messages: list[PromptMessage], stop: list[str]) -> LLMResult:
"""
invoke model
:param user_id: the user id
:param prompt_messages: the prompt messages
:param stop: the stop words
:return: the model result
"""
# invoke model
return ModelInvocationUtils.invoke(
user_id=user_id,
tenant_id=self.runtime.tenant_id or "",
tool_type="builtin",
tool_name=self.entity.identity.name,
prompt_messages=prompt_messages,
)
def tool_provider_type(self) -> ToolProviderType:
return ToolProviderType.BUILT_IN
def get_max_tokens(self) -> int:
"""
get max tokens
:return: the max tokens
"""
if self.runtime is None:
raise ValueError("runtime is required")
return ModelInvocationUtils.get_max_llm_context_tokens(
tenant_id=self.runtime.tenant_id or "",
)
def get_prompt_tokens(self, prompt_messages: list[PromptMessage]) -> int:
"""
get prompt tokens
:param prompt_messages: the prompt messages
:return: the tokens
"""
if self.runtime is None:
raise ValueError("runtime is required")
return ModelInvocationUtils.calculate_tokens(
tenant_id=self.runtime.tenant_id or "", prompt_messages=prompt_messages
)
def summary(self, user_id: str, content: str) -> str:
max_tokens = self.get_max_tokens()
if self.get_prompt_tokens(prompt_messages=[UserPromptMessage(content=content)]) < max_tokens * 0.6:
return content
def get_prompt_tokens(content: str) -> int:
return self.get_prompt_tokens(
prompt_messages=[SystemPromptMessage(content=_SUMMARY_PROMPT), UserPromptMessage(content=content)]
)
def summarize(content: str) -> str:
summary = self.invoke_model(
user_id=user_id,
prompt_messages=[SystemPromptMessage(content=_SUMMARY_PROMPT), UserPromptMessage(content=content)],
stop=[],
)
assert isinstance(summary.message.content, str)
return summary.message.content
lines = content.split("\n")
new_lines = []
# split long line into multiple lines
for i in range(len(lines)):
line = lines[i]
if not line.strip():
continue
if len(line) < max_tokens * 0.5:
new_lines.append(line)
elif get_prompt_tokens(line) > max_tokens * 0.7:
while get_prompt_tokens(line) > max_tokens * 0.7:
new_lines.append(line[: int(max_tokens * 0.5)])
line = line[int(max_tokens * 0.5) :]
new_lines.append(line)
else:
new_lines.append(line)
# merge lines into messages with max tokens
messages: list[str] = []
for j in new_lines:
if len(messages) == 0:
messages.append(j)
else:
if len(messages[-1]) + len(j) < max_tokens * 0.5:
messages[-1] += j
if get_prompt_tokens(messages[-1] + j) > max_tokens * 0.7:
messages.append(j)
else:
messages[-1] += j
summaries = []
for i in range(len(messages)):
message = messages[i]
summary = summarize(message)
summaries.append(summary)
result = "\n".join(summaries)
if self.get_prompt_tokens(prompt_messages=[UserPromptMessage(content=result)]) > max_tokens * 0.7:
return self.summary(user_id=user_id, content=result)
return result