選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

base.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #
  2. # Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import logging
  17. import re
  18. import time
  19. from copy import deepcopy
  20. from functools import partial
  21. from typing import TypedDict, List, Any
  22. from agent.component.base import ComponentParamBase, ComponentBase
  23. from api.utils import hash_str2int
  24. from rag.llm.chat_model import ToolCallSession
  25. from rag.prompts.prompts import kb_prompt
  26. from rag.utils.mcp_tool_call_conn import MCPToolCallSession
  27. class ToolParameter(TypedDict):
  28. type: str
  29. description: str
  30. displayDescription: str
  31. enum: List[str]
  32. required: bool
  33. class ToolMeta(TypedDict):
  34. name: str
  35. displayName: str
  36. description: str
  37. displayDescription: str
  38. parameters: dict[str, ToolParameter]
  39. class LLMToolPluginCallSession(ToolCallSession):
  40. def __init__(self, tools_map: dict[str, object], callback: partial):
  41. self.tools_map = tools_map
  42. self.callback = callback
  43. def tool_call(self, name: str, arguments: dict[str, Any]) -> Any:
  44. assert name in self.tools_map, f"LLM tool {name} does not exist"
  45. if isinstance(self.tools_map[name], MCPToolCallSession):
  46. resp = self.tools_map[name].tool_call(name, arguments, 60)
  47. else:
  48. resp = self.tools_map[name].invoke(**arguments)
  49. self.callback(name, arguments, resp)
  50. return resp
  51. def get_tool_obj(self, name):
  52. return self.tools_map[name]
  53. class ToolParamBase(ComponentParamBase):
  54. def __init__(self):
  55. #self.meta:ToolMeta = None
  56. super().__init__()
  57. self._init_inputs()
  58. self._init_attr_by_meta()
  59. def _init_inputs(self):
  60. self.inputs = {}
  61. for k,p in self.meta["parameters"].items():
  62. self.inputs[k] = deepcopy(p)
  63. def _init_attr_by_meta(self):
  64. for k,p in self.meta["parameters"].items():
  65. if not hasattr(self, k):
  66. setattr(self, k, p.get("default"))
  67. def get_meta(self):
  68. params = {}
  69. for k, p in self.meta["parameters"].items():
  70. params[k] = {
  71. "type": p["type"],
  72. "description": p["description"]
  73. }
  74. if "enum" in p:
  75. params[k]["enum"] = p["enum"]
  76. desc = self.meta["description"]
  77. if hasattr(self, "description"):
  78. desc = self.description
  79. function_name = self.meta["name"]
  80. if hasattr(self, "function_name"):
  81. function_name = self.function_name
  82. return {
  83. "type": "function",
  84. "function": {
  85. "name": function_name,
  86. "description": desc,
  87. "parameters": {
  88. "type": "object",
  89. "properties": params,
  90. "required": [k for k, p in self.meta["parameters"].items() if p["required"]]
  91. }
  92. }
  93. }
  94. class ToolBase(ComponentBase):
  95. def __init__(self, canvas, id, param: ComponentParamBase):
  96. from agent.canvas import Canvas # Local import to avoid cyclic dependency
  97. assert isinstance(canvas, Canvas), "canvas must be an instance of Canvas"
  98. self._canvas = canvas
  99. self._id = id
  100. self._param = param
  101. self._param.check()
  102. def get_meta(self) -> dict[str, Any]:
  103. return self._param.get_meta()
  104. def invoke(self, **kwargs):
  105. self.set_output("_created_time", time.perf_counter())
  106. try:
  107. res = self._invoke(**kwargs)
  108. except Exception as e:
  109. self._param.outputs["_ERROR"] = {"value": str(e)}
  110. logging.exception(e)
  111. res = str(e)
  112. self._param.debug_inputs = []
  113. self.set_output("_elapsed_time", time.perf_counter() - self.output("_created_time"))
  114. return res
  115. def _retrieve_chunks(self, res_list: list, get_title, get_url, get_content, get_score=None):
  116. chunks = []
  117. aggs = []
  118. for r in res_list:
  119. content = get_content(r)
  120. if not content:
  121. continue
  122. content = re.sub(r"!?\[[a-z]+\]\(data:image/png;base64,[ 0-9A-Za-z/_=+-]+\)", "", content)
  123. content = content[:10000]
  124. if not content:
  125. continue
  126. id = str(hash_str2int(content))
  127. title = get_title(r)
  128. url = get_url(r)
  129. score = get_score(r) if get_score else 1
  130. chunks.append({
  131. "chunk_id": id,
  132. "content": content,
  133. "doc_id": id,
  134. "docnm_kwd": title,
  135. "similarity": score,
  136. "url": url
  137. })
  138. aggs.append({
  139. "doc_name": title,
  140. "doc_id": id,
  141. "count": 1,
  142. "url": url
  143. })
  144. self._canvas.add_refernce(chunks, aggs)
  145. self.set_output("formalized_content", "\n".join(kb_prompt({"chunks": chunks, "doc_aggs": aggs}, 200000, True)))
  146. def thoughts(self) -> str:
  147. return self._canvas.get_component_name(self._id) + " is running..."