From 1412fa9ed157d9df1bd1935a05c49ae1a1056a9c Mon Sep 17 00:00:00 2001 From: "jinli.yl" Date: Wed, 22 Oct 2025 21:53:59 +0800 Subject: [PATCH 1/2] feat(reme): implement ReMeApp and decouple flowllm dependencies --- README.md | 161 ++++++++++ cookbook/simple_demo/import_usage_demo.py | 274 ++++++++++++++++++ docs/future_work.md | 18 +- pyproject.toml | 2 + reme_ai/__init__.py | 9 +- reme_ai/agent/tools/llm_mock_search_op.py | 7 +- reme_ai/agent/tools/use_mock_search_op.py | 7 +- reme_ai/app.py | 26 +- reme_ai/config/default.yaml | 30 +- .../agentscope_runtime_memory_service.py | 5 +- .../summary/tool/parse_tool_call_result_op.py | 7 +- .../summary/tool/summary_tool_memory_op.py | 5 +- 12 files changed, 508 insertions(+), 43 deletions(-) create mode 100644 cookbook/simple_demo/import_usage_demo.py diff --git a/README.md b/README.md index 86506c61..f4119f9e 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,45 @@ response = requests.post("http://localhost:8002/retrieve_task_memory", json={ }) ``` +
+Python import version + +```python +import asyncio +from reme_ai import ReMeApp + +async def main(): + async with ReMeApp() as app: + # Experience Summarizer: Learn from execution trajectories + result = await app.async_execute( + name="summary_task_memory", + workspace_id="task_workspace", + trajectories=[ + { + "messages": [ + {"role": "user", "content": "Help me create a project plan"} + ], + "score": 1.0 + } + ] + ) + print(result) + + # Retriever: Get relevant memories + result = await app.async_execute( + name="retrieve_task_memory", + workspace_id="task_workspace", + query="How to efficiently manage project progress?", + top_k=1 + ) + print(result) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +
+
curl version @@ -255,6 +294,46 @@ response = requests.post("http://localhost:8002/retrieve_personal_memory", json= }) ``` +
+Python import version + +```python +import asyncio +from reme_ai import ReMeApp + +async def main(): + async with ReMeApp() as app: + # Memory Integration: Learn from user interactions + result = await app.async_execute( + name="summary_personal_memory", + workspace_id="task_workspace", + trajectories=[ + { + "messages": [ + {"role": "user", "content": "I like to drink coffee while working in the morning"}, + {"role": "assistant", + "content": "I understand, you prefer to start your workday with coffee to stay energized"} + ] + } + ] + ) + print(result) + + # Memory Retrieval: Get personal memory fragments + result = await app.async_execute( + name="retrieve_personal_memory", + workspace_id="task_workspace", + query="What are the user's work habits?", + top_k=5 + ) + print(result) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +
+
curl version @@ -359,6 +438,55 @@ response = requests.post("http://localhost:8002/retrieve_tool_memory", json={ }) ``` +
+Python import version + +```python +import asyncio +from reme_ai import ReMeApp + +async def main(): + async with ReMeApp() as app: + # Record tool execution results + result = await app.async_execute( + name="add_tool_call_result", + workspace_id="tool_workspace", + tool_call_results=[ + { + "create_time": "2025-10-21 10:30:00", + "tool_name": "web_search", + "input": {"query": "Python asyncio tutorial", "max_results": 10}, + "output": "Found 10 relevant results...", + "token_cost": 150, + "success": True, + "time_cost": 2.3 + } + ] + ) + print(result) + + # Generate usage guidelines from history + result = await app.async_execute( + name="summary_tool_memory", + workspace_id="tool_workspace", + tool_names="web_search" + ) + print(result) + + # Retrieve tool guidelines before use + result = await app.async_execute( + name="retrieve_tool_memory", + workspace_id="tool_workspace", + tool_names="web_search" + ) + print(result) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +
+
curl version @@ -489,6 +617,39 @@ response = requests.post("http://localhost:8002/retrieve_task_memory", json={ }) ``` +
+Python import version + +```python +import asyncio +from reme_ai import ReMeApp + +async def main(): + async with ReMeApp() as app: + # Load pre-built memories + result = await app.async_execute( + name="vector_store", + workspace_id="appworld", + action="load", + path="./docs/library/" + ) + print(result) + + # Query relevant memories + result = await app.async_execute( + name="retrieve_task_memory", + workspace_id="appworld", + query="How to navigate to settings and update user profile?", + top_k=1 + ) + print(result) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +
+ ## ๐Ÿงช Experiments ### ๐ŸŒ [Appworld Experiment](docs/cookbook/appworld/quickstart.md) diff --git a/cookbook/simple_demo/import_usage_demo.py b/cookbook/simple_demo/import_usage_demo.py new file mode 100644 index 00000000..b51325ca --- /dev/null +++ b/cookbook/simple_demo/import_usage_demo.py @@ -0,0 +1,274 @@ +import asyncio + +from reme_ai import ReMeApp + + +# ============================================ +# Task Memory Management Examples +# ============================================ + +async def summary_task_memory(): + """ + Experience Summarizer: Learn from execution trajectories + + curl -X POST http://localhost:8002/summary_task_memory \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "task_workspace", + "trajectories": [ + {"messages": [{"role": "user", "content": "Help me create a project plan"}], "score": 1.0} + ] + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="summary_task_memory", + workspace_id="task_workspace", + trajectories=[ + { + "messages": [ + {"role": "user", "content": "Help me create a project plan"} + ], + "score": 1.0 + } + ] + ) + print("Summary Task Memory Result:") + print(result) + + +async def retrieve_task_memory(): + """ + Retriever: Get relevant memories + + curl -X POST http://localhost:8002/retrieve_task_memory \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "task_workspace", + "query": "How to efficiently manage project progress?", + "top_k": 1 + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="retrieve_task_memory", + workspace_id="task_workspace", + query="How to efficiently manage project progress?", + top_k=1 + ) + print("Retrieve Task Memory Result:") + print(result) + + +# ============================================ +# Personal Memory Management Examples +# ============================================ + +async def summary_personal_memory(): + """ + Memory Integration: Learn from user interactions + + curl -X POST http://localhost:8002/summary_personal_memory \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "task_workspace", + "trajectories": [ + {"messages": [ + {"role": "user", "content": "I like to drink coffee while working in the morning"}, + {"role": "assistant", "content": "I understand, you prefer to start your workday with coffee to stay energized"} + ]} + ] + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="summary_personal_memory", + workspace_id="task_workspace", + trajectories=[ + { + "messages": [ + {"role": "user", "content": "I like to drink coffee while working in the morning"}, + {"role": "assistant", + "content": "I understand, you prefer to start your workday with coffee to stay energized"} + ] + } + ] + ) + print("Summary Personal Memory Result:") + print(result) + + +async def retrieve_personal_memory(): + """ + Memory Retrieval: Get personal memory fragments + + curl -X POST http://localhost:8002/retrieve_personal_memory \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "task_workspace", + "query": "What are the users work habits?", + "top_k": 5 + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="retrieve_personal_memory", + workspace_id="task_workspace", + query="What are the user's work habits?", + top_k=5 + ) + print("Retrieve Personal Memory Result:") + print(result) + + +# ============================================ +# Tool Memory Management Examples +# ============================================ + +async def add_tool_call_result(): + """ + Record tool execution results + + curl -X POST http://localhost:8002/add_tool_call_result \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "tool_workspace", + "tool_call_results": [ + { + "create_time": "2025-10-21 10:30:00", + "tool_name": "web_search", + "input": {"query": "Python asyncio tutorial", "max_results": 10}, + "output": "Found 10 relevant results...", + "token_cost": 150, + "success": true, + "time_cost": 2.3 + } + ] + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="add_tool_call_result", + workspace_id="tool_workspace", + tool_call_results=[ + { + "create_time": "2025-10-21 10:30:00", + "tool_name": "web_search", + "input": {"query": "Python asyncio tutorial", "max_results": 10}, + "output": "Found 10 relevant results...", + "token_cost": 150, + "success": True, + "time_cost": 2.3 + } + ] + ) + print("Add Tool Call Result:") + print(result) + + +async def summary_tool_memory(): + """ + Generate usage guidelines from history + + curl -X POST http://localhost:8002/summary_tool_memory \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "tool_workspace", + "tool_names": "web_search" + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="summary_tool_memory", + workspace_id="tool_workspace", + tool_names="web_search" + ) + print("Summary Tool Memory Result:") + print(result) + + +async def retrieve_tool_memory(): + """ + Retrieve tool guidelines before use + + curl -X POST http://localhost:8002/retrieve_tool_memory \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "tool_workspace", + "tool_names": "web_search" + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="retrieve_tool_memory", + workspace_id="tool_workspace", + tool_names="web_search" + ) + print("Retrieve Tool Memory Result:") + print(result) + + +# ============================================ +# Vector Store Management Example +# ============================================ + +async def load_vector_store(): + """ + Load pre-built memories + + curl -X POST http://localhost:8002/vector_store \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "appworld", + "action": "load", + "path": "./docs/library/" + }' + """ + async with ReMeApp() as app: + result = await app.async_execute( + name="vector_store", + workspace_id="appworld", + action="load", + path="./docs/library/" + ) + print("Load Vector Store Result:") + print(result) + + +# ============================================ +# Main Execution +# ============================================ + +async def main(): + """Run all examples""" + print("=" * 60) + print("Task Memory Examples") + print("=" * 60) + await summary_task_memory() + print("\n") + await retrieve_task_memory() + + print("\n" + "=" * 60) + print("Personal Memory Examples") + print("=" * 60) + await summary_personal_memory() + print("\n") + await retrieve_personal_memory() + + print("\n" + "=" * 60) + print("Tool Memory Examples") + print("=" * 60) + await add_tool_call_result() + print("\n") + await summary_tool_memory() + print("\n") + await retrieve_tool_memory() + + print("\n" + "=" * 60) + print("Vector Store Examples") + print("=" * 60) + await load_vector_store() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/future_work.md b/docs/future_work.md index 951a95c1..807abfed 100644 --- a/docs/future_work.md +++ b/docs/future_work.md @@ -2,17 +2,17 @@ - [ ] P0 ReMe documentation style migration: Recommend using the same doc and jupyter structure as Agentscope Runtime @jiaji - [ ] P0 ReMe integration with agentscope Personal/Task/Tool @jinli -- [ ] P0 ReMe sample library examples [show case](https://github.com/agentscope-ai/agentscope-samples/tree/main/functionality/long_term_memory_mem0) -- [ ] P0 Decouple flowllm dependencies -- [ ] P0 ReMe support for import, improve code documentation -- [ ] P1 ReMe integration with asio tool_memory -- [ ] P2 ReMe integration with agentscope-Runtime tool_memory +- [ ] P0 ReMe sample library examples [show case](https://github.com/agentscope-ai/agentscope-samples/tree/main/functionality/long_term_memory_mem0) @jinli +- [ ] P0 Decouple flowllm dependencies @jinli +- [ ] P0 ReMe support for import, improve code documentation @jinli +- [ ] P1 ReMe integration with asio tool_memory @jinli +- [ ] P2 ReMe integration with agentscope-Runtime tool_memory @jinli - [ ] P0 Task Memory Research Paper @zhoyin -- [ ] P1 Context interface definition +- [ ] P1 Context interface definition @jinli -- [ ] P2 Database layer interface unification -- [ ] P2 Automatic Tool Exploration Mode -- [ ] P2 Mem-Agent Exploration +- [ ] P2 Database layer interface unification @jinli +- [ ] P2 Automatic Tool Exploration Mode @wangcan +- [ ] P2 Mem-Agent Exploration @weikang - [ ] P2 Desktop Pet Personal Assistant diff --git a/pyproject.toml b/pyproject.toml index 83a62a11..69652fe4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,3 +41,5 @@ reme_ai = [ [project.scripts] reme = "reme_ai.app:main" + +# python -m build && twine upload dist/* \ No newline at end of file diff --git a/reme_ai/__init__.py b/reme_ai/__init__.py index 464e80d3..c84f6720 100644 --- a/reme_ai/__init__.py +++ b/reme_ai/__init__.py @@ -1,11 +1,4 @@ -import warnings - -from pydantic.warnings import PydanticDeprecatedSince20 - -warnings.filterwarnings("ignore", category=DeprecationWarning, module="websockets") -warnings.filterwarnings("ignore", category=DeprecationWarning, module="uvicorn") -warnings.filterwarnings("ignore", category=PydanticDeprecatedSince20) - +from .app import ReMeApp from . import agent from . import retrieve from . import summary diff --git a/reme_ai/agent/tools/llm_mock_search_op.py b/reme_ai/agent/tools/llm_mock_search_op.py index 7ba53415..a2219064 100644 --- a/reme_ai/agent/tools/llm_mock_search_op.py +++ b/reme_ai/agent/tools/llm_mock_search_op.py @@ -3,13 +3,12 @@ import json import random from typing import Dict, Any -from loguru import logger - from flowllm.context import FlowContext, C from flowllm.enumeration.role import Role from flowllm.op.base_async_tool_op import BaseAsyncToolOp from flowllm.schema.message import Message from flowllm.schema.tool_call import ToolCall +from loguru import logger @C.register_op() @@ -254,9 +253,9 @@ class LLMMockSearchOp(BaseAsyncToolOp): async def async_main(): - from flowllm.app import FlowLLMApp + from reme_ai.app import ReMeApp - async with FlowLLMApp(load_default_config=True): + async with ReMeApp(): # Test with different query types test_queries = [ "What is the capital of France?", # Simple diff --git a/reme_ai/agent/tools/use_mock_search_op.py b/reme_ai/agent/tools/use_mock_search_op.py index 5c043d7a..67247f97 100644 --- a/reme_ai/agent/tools/use_mock_search_op.py +++ b/reme_ai/agent/tools/use_mock_search_op.py @@ -8,7 +8,6 @@ from flowllm.op.base_async_tool_op import BaseAsyncToolOp from flowllm.schema.message import Message from flowllm.schema.tool_call import ToolCall from flowllm.utils.timer import Timer -from flowllm.utils.token_utils import TokenCounter from loguru import logger from reme_ai.agent.tools.mock_search_tools import SearchToolA, SearchToolB, SearchToolC @@ -99,7 +98,7 @@ class UseMockSearchOp(BaseAsyncToolOp): selected_op_output = json.loads(selected_op.output) content = selected_op_output["content"] success = selected_op_output["success"] - token_cost = TokenCounter().count(content) + token_cost = len(content) // 4 # Estimate using a method where every 4 characters constitute one token. time_cost = timer.time_cost @@ -118,9 +117,9 @@ class UseMockSearchOp(BaseAsyncToolOp): async def async_main(): - from flowllm.app import FlowLLMApp + from reme_ai.app import ReMeApp - async with FlowLLMApp(load_default_config=True): + async with ReMeApp(): test_queries = [ "What is the capital of France?", "How does quantum computing work?", diff --git a/reme_ai/app.py b/reme_ai/app.py index 9945b730..7c5e18e8 100644 --- a/reme_ai/app.py +++ b/reme_ai/app.py @@ -1,15 +1,33 @@ +import asyncio import sys +from typing import List -from flowllm.app import FlowLLMApp +from flowllm import FlowLLMApp, C +from flowllm.schema.flow_response import FlowResponse +from loguru import logger from reme_ai.config.config_parser import ConfigParser +class ReMeApp(FlowLLMApp): + + def __init__(self, args: List[str] = None): + super().__init__(args=args, parser=ConfigParser) + self.registered_flows = C.flow_dict.keys() + logger.info(f"registered_flows={self.registered_flows}") + + async def async_execute(self, name: str, **kwargs) -> dict: + assert name in self.registered_flows, f"Invalid flow_name={name} !" + result: FlowResponse = await self.async_execute_flow(name=name, **kwargs) + return result.model_dump() + + def execute(self, name: str, **kwargs) -> dict: + return asyncio.run(self.async_execute(name=name, **kwargs)) + + def main(): - with FlowLLMApp(args=sys.argv[1:], parser=ConfigParser) as app: + with ReMeApp(args=sys.argv[1:]) as app: app.run_service() if __name__ == "__main__": main() - -# python -m build && twine upload dist/* diff --git a/reme_ai/config/default.yaml b/reme_ai/config/default.yaml index 992b0117..1314bcfb 100644 --- a/reme_ai/config/default.yaml +++ b/reme_ai/config/default.yaml @@ -185,12 +185,6 @@ llm: params: temperature: 0.6 - wk1: - backend: openai_compatible - model_name: qwen3-30b-a3b-instruct-2507 - params: - temperature: 0.6 - qwen3_30b_instruct: backend: openai_compatible model_name: qwen3-30b-a3b-instruct-2507 @@ -199,6 +193,30 @@ llm: backend: openai_compatible model_name: qwen3-30b-a3b-thinking-2507 + qwen3_235b_instruct: + backend: openai_compatible + model_name: qwen3-235b-a22b-instruct-2507 + + qwen3_235b_thinking: + backend: openai_compatible + model_name: qwen3-235b-a22b-thinking-2507 + + qwen3_80b_instruct: + backend: openai_compatible + model_name: qwen3-next-80b-a3b-instruct + + qwen3_80b_thinking: + backend: openai_compatible + model_name: qwen3-next-80b-a3b-thinking + + qwen3_max_instruct: + backend: openai_compatible + model_name: qwen3-max + + qwen25_max_instruct: + backend: openai_compatible + model_name: qwen-max-2025-01-25 + embedding_model: default: backend: openai_compatible diff --git a/reme_ai/service/agentscope_runtime_memory_service.py b/reme_ai/service/agentscope_runtime_memory_service.py index e4c21713..c9c3f3d5 100644 --- a/reme_ai/service/agentscope_runtime_memory_service.py +++ b/reme_ai/service/agentscope_runtime_memory_service.py @@ -1,16 +1,15 @@ from abc import abstractmethod, ABC from typing import Optional, Dict, Any -from flowllm import FlowLLMApp from pydantic import Field -from reme_ai.config.config_parser import ConfigParser +from reme_ai.app import ReMeApp class AgentscopeRuntimeMemoryService(ABC): def __init__(self): - self.app = FlowLLMApp(parser=ConfigParser, load_default_config=True) + self.app = ReMeApp() self.session_id_dict: dict = {} def add_session_memory_id(self, session_id: str, memory_id): diff --git a/reme_ai/summary/tool/parse_tool_call_result_op.py b/reme_ai/summary/tool/parse_tool_call_result_op.py index 75137ee5..e40c2812 100644 --- a/reme_ai/summary/tool/parse_tool_call_result_op.py +++ b/reme_ai/summary/tool/parse_tool_call_result_op.py @@ -134,10 +134,11 @@ class ParseToolCallResultOp(BaseAsyncOp): async def main(): """Simple test for ParseToolCallResultOp""" - from flowllm.app import FlowLLMApp from datetime import datetime - - async with FlowLLMApp(load_default_config=True): + + from reme_ai.app import ReMeApp + + async with ReMeApp(): op = ParseToolCallResultOp() # Create simple test data diff --git a/reme_ai/summary/tool/summary_tool_memory_op.py b/reme_ai/summary/tool/summary_tool_memory_op.py index 70976bc5..893337ea 100644 --- a/reme_ai/summary/tool/summary_tool_memory_op.py +++ b/reme_ai/summary/tool/summary_tool_memory_op.py @@ -195,13 +195,14 @@ class SummaryToolMemoryOp(BaseAsyncOp): async def main(): - from flowllm.app import FlowLLMApp from reme_ai.summary.tool.parse_tool_call_result_op import ParseToolCallResultOp from reme_ai.vector_store.update_vector_store_op import UpdateVectorStoreOp from datetime import datetime, timedelta import random - async with FlowLLMApp(load_default_config=True): + from reme_ai.app import ReMeApp + + async with ReMeApp(): workspace_id = "test_workspace_complex" tool_name = "web_search_tool" From da63cf9eae2db83b05d9af6cfd856f3390f4b029 Mon Sep 17 00:00:00 2001 From: "jinli.yl" Date: Wed, 22 Oct 2025 22:02:44 +0800 Subject: [PATCH 2/2] chore(version): bump version to 0.1.10.2 --- README.md | 3 ++- pyproject.toml | 2 +- reme_ai/__init__.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f4119f9e..0fbada48 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Python Version - PyPI Version + PyPI Version License GitHub Stars

@@ -28,6 +28,7 @@ Personal memory helps "**understand user preferences**", task memory helps agent ## ๐Ÿ“ฐ Latest Updates +- **[2025-10]** ๐Ÿš€ ReMe v0.1.10.2 released! Core enhancement: direct Python import support. You can now use ReMe without starting an HTTP or MCP service - simply `from reme_ai import ReMeApp` and call methods directly in your Python code. - **[2025-10]** ๐Ÿ”ง Tool Memory support is now available! Enables data-driven tool selection and parameter optimization through historical performance tracking. Check out the [Tool Memory Guide](docs/tool_memory/tool_memory.md) and [benchmark results](docs/tool_memory/tool_bench.md). - **[2025-09]** ๐ŸŽ‰ ReMe v0.1.9 has been officially released, adding support for asynchronous operations. It has also been integrated into the memory service of agentscope-runtime. diff --git a/pyproject.toml b/pyproject.toml index 69652fe4..cdb2ed86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "reme_ai" -version = "0.1.10.1" +version = "0.1.10.2" description = "Remember me" authors = [ { name = "jinli.yl", email = "jinli.yl@alibaba-inc.com" }, diff --git a/reme_ai/__init__.py b/reme_ai/__init__.py index c84f6720..68eab1d5 100644 --- a/reme_ai/__init__.py +++ b/reme_ai/__init__.py @@ -4,4 +4,4 @@ from . import retrieve from . import summary from . import vector_store -__version__ = "0.1.10.1" +__version__ = "0.1.10.2"