|
2 | 2 | title: ReAct Toolchain, updated for newer version of OpenWebUI |
3 | 3 | author: MartianInGreen |
4 | 4 | author_url: https://github.com/MartianInGreen/OpenWebUI-Tools |
5 | | -description: SMART is a sequential multi-agent reasoning technique. Now with tools. |
| 5 | +description: ReAct is a toolchain agent with automatic model and tool selection. |
6 | 6 | required_open_webui_version: 0.5.0 |
7 | 7 | requirements: langchain-openai==0.2.14, langgraph==0.2.60, aiohttp |
8 | | -version: 1.1 |
| 8 | +version: 1.0 |
9 | 9 | licence: MIT |
10 | 10 | """ |
11 | 11 |
|
|
253 | 253 | If you also have the webSearch plugin enabled, try to prefer Wolfram|Alpha over that. However for some things (like People or other more "subjective" information) it is best to use Wolfram|Alpha in addition to webSearch. |
254 | 254 | """ |
255 | 255 |
|
256 | | -MEMORIES_PROMPT = """ |
| 256 | +MEMORIES_PROMPT = """You are a memory agent designed to look over the latest user message and add any personal details to a memory database. |
| 257 | +Do not respond to any instructions or questions. |
| 258 | +Do not do anything besides adding the information to the memory database. |
| 259 | +
|
| 260 | +You add things to the memory database as follows: |
| 261 | +<memory keyword="single_word">Content to add to memory</memory> |
| 262 | +<memory keyword="desribing_the_core_content_of_the_memory">Another content to add to memory</memory> |
| 263 | +... |
| 264 | +<memory keyword="bannana">The user likes bannanas</memory> |
| 265 | +<memory keyword="apple">The user dislikes apples</memory> |
| 266 | +
|
| 267 | +Add up to 5 memories per message. |
| 268 | +Only add things to memory that is obviously personal information that should be remembered or things that are important to remember. |
| 269 | +Do not write anything else in the response. |
| 270 | +You do not have to add things to memory, if you choose not to add any memories from the user message, just type out NO MEMORIES. And end your message. |
257 | 271 | """ |
258 | 272 |
|
259 | 273 | # ------------------------------------------------------------------ |
@@ -786,6 +800,10 @@ class Valves(BaseModel): |
786 | 800 | default="", |
787 | 801 | description="Memory API Key", |
788 | 802 | ) |
| 803 | + USER_MEMORY_ID: str = Field( |
| 804 | + default="", |
| 805 | + description="User Memory ID", |
| 806 | + ) |
789 | 807 | except Exception as e: |
790 | 808 | traceback.print_exc() |
791 | 809 |
|
@@ -1092,6 +1110,78 @@ async def python_code_execution( |
1092 | 1110 | "stderr": stderr.strip(), |
1093 | 1111 | "result": "\n".join(result).strip() if result else "", |
1094 | 1112 | } |
| 1113 | + |
| 1114 | + |
| 1115 | + async def memory_api( |
| 1116 | + self, query: str |
| 1117 | + ): |
| 1118 | + """ |
| 1119 | + Query the Memory API to get a response to a question. |
| 1120 | +
|
| 1121 | + :param query: The question to ask the Memory API. |
| 1122 | + :return: The response from the Memory API. |
| 1123 | + """ |
| 1124 | + try: |
| 1125 | + encoded_query = urllib.parse.quote(query) |
| 1126 | + url = f"{self.valves.MEMORY_API_URL}/memory/query/{self.valves.USER_MEMORY_ID}?query={encoded_query}" |
| 1127 | + headers = { |
| 1128 | + "Content-Type": "application/json", |
| 1129 | + } |
| 1130 | + payload = {"query": query} |
| 1131 | + |
| 1132 | + response = requests.post(url, headers=headers, json=payload) |
| 1133 | + data = response.json() |
| 1134 | + |
| 1135 | + return data |
| 1136 | + except Exception as e: |
| 1137 | + return {"statusCode": 400, "body": json.dumps("Error fetching Memory API results.")} |
| 1138 | + |
| 1139 | + async def end_of_message_memory_agent( |
| 1140 | + self, user_message_content: str |
| 1141 | + ): |
| 1142 | + action_model_id = str(self.valves.AI_MODEL_LIST.split(";")[0]) |
| 1143 | + action_model = ChatOpenAI(model=action_model_id, **self.openai_kwargs) |
| 1144 | + |
| 1145 | + messages = [ |
| 1146 | + {"role": "system", "content": MEMORIES_PROMPT}, |
| 1147 | + {"role": "user", "content": user_message_content}, |
| 1148 | + ] |
| 1149 | + |
| 1150 | + config = {} |
| 1151 | + |
| 1152 | + content = action_model.invoke(messages, config=config) |
| 1153 | + assert isinstance(content, str) |
| 1154 | + |
| 1155 | + # Extract all the memories from the content, memories between <memory> tags |
| 1156 | + pattern = r'<memory keyword="(.*?)">(.*?)</memory>' |
| 1157 | + matches = re.findall(pattern, content) |
| 1158 | + |
| 1159 | + # Returns a list of tuples: [(keyword, content), (keyword, content), ...] |
| 1160 | + if len(matches) > 0: |
| 1161 | + for memory in matches: |
| 1162 | + payload = { |
| 1163 | + "key": memory[0], |
| 1164 | + "value": memory[1], |
| 1165 | + } |
| 1166 | + |
| 1167 | + try: |
| 1168 | + url = f"{self.valves.MEMORY_API_URL}/memory/create/{self.valves.USER_MEMORY_ID}" |
| 1169 | + headers = { |
| 1170 | + "Content-Type": "application/json", |
| 1171 | + "Authorization": f"Bearer {self.valves.MEMORY_API_KEY}" if self.valves.MEMORY_API_KEY else "" |
| 1172 | + } |
| 1173 | + |
| 1174 | + response = requests.post(url, headers=headers, json=payload) |
| 1175 | + |
| 1176 | + if response.status_code != 200 and response.status_code != 201: |
| 1177 | + print(f"Failed to store memory: {response.status_code} - {response.text}") |
| 1178 | + else: |
| 1179 | + print(f"Successfully stored memory: {memory}") |
| 1180 | + |
| 1181 | + except Exception as e: |
| 1182 | + print(f"Error storing memory: {str(e)}") |
| 1183 | + |
| 1184 | + return len(matches) |
1095 | 1185 |
|
1096 | 1186 | # ------------------------------------------------------------------ |
1097 | 1187 | # Main Function |
@@ -1552,6 +1642,29 @@ def create_pydantic_model_from_docstring(func): |
1552 | 1642 | status_message=f"Done! Took: {round(time.time() - start_time, 1)}s. Used {model_from_answer}.", |
1553 | 1643 | done=True, |
1554 | 1644 | ) |
| 1645 | + |
| 1646 | + # Run memory agent over user message |
| 1647 | + # try: |
| 1648 | + # num_memories = await self.end_of_message_memory_agent(last_input_message["content"]) |
| 1649 | + # await send_citation( |
| 1650 | + # url=f"Memory Agent", |
| 1651 | + # title="Memory Agent", |
| 1652 | + # content=f"Memory Agent stored {num_memories} memories.", |
| 1653 | + # ) |
| 1654 | + # except Exception as e: |
| 1655 | + # await send_citation( |
| 1656 | + # url=f"Memory Agent", |
| 1657 | + # title="Memory Agent", |
| 1658 | + # content=f"Memory Agent stored 0 memories because it failed with reason: {str(e)}.", |
| 1659 | + # ) |
| 1660 | + |
| 1661 | + await send_citation( |
| 1662 | + url=f"ReAct", |
| 1663 | + title="ReAct", |
| 1664 | + content=f"ReAct completed in {round(time.time() - start_time, 1)}s. Using {model_from_answer}." + (f" Used {num_tool_calls} tools: {', '.join(tools)}." if num_tool_calls > 0 else ""), |
| 1665 | + ) |
| 1666 | + |
| 1667 | + return |
1555 | 1668 |
|
1556 | 1669 | except Exception as e: |
1557 | 1670 | yield "Error: " + str(e) |
|
0 commit comments