diff --git a/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/pom.xml b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/pom.xml new file mode 100644 index 0000000000..048f87b4a0 --- /dev/null +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + com.alibaba.cloud.ai + spring-ai-alibaba + ${revision} + ../../pom.xml + + + com.alibaba.cloud.ai.autoconfigure.memory.long + spring-ai-alibaba-autoconfigure-memory-long + + Spring AI Alibaba Long Memory Autoconfiguration + Spring AI Alibaba Long Memory Autoconfiguration + https://github.com/alibaba/spring-ai-alibaba + + git://github.com/alibaba/spring-ai-alibaba.git + git@github.com:alibaba/spring-ai-alibaba.git + https://github.com/alibaba/spring-ai-alibaba + + + + 17 + 17 + UTF-8 + + + + + com.alibaba.cloud.ai + spring-ai-alibaba-starter-memory-mem0 + ${revision} + + + + diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryAutoConfiguration.java b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/java/com/alibaba/cloud/ai/autoconfigure/memory/Mem0ChatMemoryAutoConfiguration.java similarity index 95% rename from community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryAutoConfiguration.java rename to auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/java/com/alibaba/cloud/ai/autoconfigure/memory/Mem0ChatMemoryAutoConfiguration.java index 8b549639b3..58ace70a80 100644 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryAutoConfiguration.java +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/java/com/alibaba/cloud/ai/autoconfigure/memory/Mem0ChatMemoryAutoConfiguration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alibaba.cloud.ai.memory.mem0.config; +package com.alibaba.cloud.ai.autoconfigure.memory; import com.alibaba.cloud.ai.memory.mem0.core.Mem0MemoryStore; import com.alibaba.cloud.ai.memory.mem0.core.Mem0ServiceClient; @@ -37,7 +37,8 @@ public class Mem0ChatMemoryAutoConfiguration { @Bean public Mem0ServiceClient mem0ServiceClient(Mem0ChatMemoryProperties properties, ResourceLoader resourceLoader) { - Mem0ServiceClient mem0ServiceClient = new Mem0ServiceClient(properties, resourceLoader); + Mem0ServiceClient mem0ServiceClient = new Mem0ServiceClient(properties.getClient(), properties.getServer(), + resourceLoader); logger.info("Initialized Mem0Service Client.success!"); // Pass the client configuration items to the Server to initialize the Mem0 // instance diff --git a/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/java/com/alibaba/cloud/ai/autoconfigure/memory/Mem0ChatMemoryProperties.java b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/java/com/alibaba/cloud/ai/autoconfigure/memory/Mem0ChatMemoryProperties.java new file mode 100644 index 0000000000..11bff31f63 --- /dev/null +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/java/com/alibaba/cloud/ai/autoconfigure/memory/Mem0ChatMemoryProperties.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.cloud.ai.autoconfigure.memory; + +import com.alibaba.cloud.ai.memory.mem0.core.Mem0Client; +import com.alibaba.cloud.ai.memory.mem0.core.Mem0Server; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = Mem0ChatMemoryProperties.MEM0_PREFIX) +public class Mem0ChatMemoryProperties { + + public static final String MEM0_PREFIX = "spring.ai.alibaba.mem0"; + + private Mem0Client client; + + private Mem0Server server; + + public Mem0Client getClient() { + return client; + } + + public void setClient(Mem0Client client) { + this.client = client; + } + + public Mem0Server getServer() { + return server; + } + + public void setServer(Mem0Server server) { + this.server = server; + } + +} diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/resources/META-INF/spring-configuration-metadata.json b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/resources/META-INF/spring-configuration-metadata.json similarity index 56% rename from community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/resources/META-INF/spring-configuration-metadata.json rename to auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/resources/META-INF/spring-configuration-metadata.json index e71b3aae55..e21a74c541 100644 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/resources/META-INF/spring-configuration-metadata.json @@ -1,142 +1,176 @@ { + "groups": [ + { + "name": "spring.ai.memory.mem0", + "type": "com.alibaba.cloud.ai.autoconfigure.memory.Mem0ChatMemoryProperties", + "sourceType": "com.alibaba.cloud.ai.autoconfigure.memory.Mem0ChatMemoryProperties" + } + ], "properties": [ { "name": "spring.ai.alibaba.mem0.client.base-url", "type": "java.lang.String", "description": "Base URL for the Mem0 client service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Client", "defaultValue": "http://localhost:8888" }, { "name": "spring.ai.alibaba.mem0.client.timeout-seconds", "type": "java.lang.Integer", "description": "Timeout in seconds for client operations.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Client", "defaultValue": 30 }, { "name": "spring.ai.alibaba.mem0.client.max-retry-attempts", "type": "java.lang.Integer", "description": "Maximum number of retry attempts for failed operations.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Client", "defaultValue": 3 }, { "name": "spring.ai.alibaba.mem0.server.version", "type": "java.lang.String", - "description": "Version of the Mem0 server to connect to." - }, + "description": "Version of the Mem0 server to connect to.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server"}, { "name": "spring.ai.alibaba.mem0.server.vector-store.provider", "type": "java.lang.String", - "description": "Provider for the vector store (e.g., qdrant, chroma, pgvector, pinecone, mongodb, milvus, baidu, upstash_vector, azure_ai_search, redis, elasticsearch, vertex_ai_vector_search, opensearch, supabase, weaviate, faiss, langchain)." + "description": "Provider for the vector store (e.g., qdrant, chroma, pgvector, pinecone, mongodb, milvus, baidu, upstash_vector, azure_ai_search, redis, elasticsearch, vertex_ai_vector_search, opensearch, supabase, weaviate, faiss, langchain).", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.vector-store.config", "type": "java.util.Map", - "description": "Configuration map for the vector store provider." + "description": "Configuration map for the vector store provider.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.provider", "type": "java.lang.String", - "description": "Provider for the graph store." + "description": "Provider for the graph store.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.config.url", "type": "java.lang.String", - "description": "URL for the graph store connection." + "description": "URL for the graph store connection.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.config.username", "type": "java.lang.String", - "description": "Username for the graph store authentication." + "description": "Username for the graph store authentication.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.config.password", "type": "java.lang.String", - "description": "Password for the graph store authentication." + "description": "Password for the graph store authentication.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.config.database", "type": "java.lang.String", - "description": "Database name for Neo4j graph store." + "description": "Database name for Neo4j graph store.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.config.base-label", "type": "java.lang.Boolean", - "description": "Base label setting for Neo4j graph store." + "description": "Base label setting for Neo4j graph store.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.graph-store.custom-prompt", "type": "java.lang.String", - "description": "Custom prompt template for graph store operations." + "description": "Custom prompt template for graph store operations.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.llm.provider", "type": "java.lang.String", - "description": "Provider for the LLM service." + "description": "Provider for the LLM service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.llm.config.api-key", "type": "java.lang.String", - "description": "API key for the LLM service." + "description": "API key for the LLM service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.llm.config.temperature", "type": "java.lang.Double", - "description": "Temperature setting for LLM responses." + "description": "Temperature setting for LLM responses.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.llm.config.model", "type": "java.lang.String", - "description": "Model name for the LLM service." + "description": "Model name for the LLM service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.llm.config.openai-base-url", "type": "java.lang.String", - "description": "Base URL for OpenAI-compatible services." + "description": "Base URL for OpenAI-compatible services.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.embedder.provider", "type": "java.lang.String", - "description": "Provider for the embedding service." + "description": "Provider for the embedding service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.embedder.config.api-key", "type": "java.lang.String", - "description": "API key for the embedding service." + "description": "API key for the embedding service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.embedder.config.model", "type": "java.lang.String", - "description": "Model name for the embedding service." + "description": "Model name for the embedding service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.embedder.config.openai-base-url", "type": "java.lang.String", - "description": "Base URL for OpenAI-compatible embedding services." + "description": "Base URL for OpenAI-compatible embedding services.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.history-db-path", "type": "java.lang.String", - "description": "Path to the history database." + "description": "Path to the history database.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.project.custom-categories", "type": "java.lang.String", - "description": "Custom categories for the project." + "description": "Custom categories for the project.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.project.custom-instructions", "type": "java.lang.String", - "description": "Custom instructions for the project." + "description": "Custom instructions for the project.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.custom-fact-extraction-prompt", "type": "java.lang.String", - "description": "Custom prompt for fact extraction." + "description": "Custom prompt for fact extraction.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" }, { "name": "spring.ai.alibaba.mem0.server.custom-update-memory-prompt", "type": "java.lang.String", - "description": "Custom prompt for memory updates." + "description": "Custom prompt for memory updates.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" } - ] + ], + "hints": [] } diff --git a/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000000..2efa5ac4f9 --- /dev/null +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory-long/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,16 @@ +# +# Copyright 2025-2025 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +com.alibaba.cloud.ai.autoconfigure.memory.Mem0ChatMemoryAutoConfiguration diff --git a/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring-configuration-metadata.json b/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring-configuration-metadata.json index 04b999c32e..ef6e95179b 100644 --- a/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring-configuration-metadata.json @@ -55,6 +55,11 @@ "name": "spring.ai.memory.tablestore", "type": "com.alibaba.cloud.ai.autoconfigure.memory.TablestoreChatMemoryProperties", "sourceType": "com.alibaba.cloud.ai.autoconfigure.memory.TablestoreChatMemoryProperties" + }, + { + "name": "spring.ai.memory.mem0", + "type": "com.alibaba.cloud.ai.autoconfigure.memory.Mem0ChatMemoryProperties", + "sourceType": "com.alibaba.cloud.ai.autoconfigure.memory.Mem0ChatMemoryProperties" } ], "properties": [ @@ -325,6 +330,170 @@ "type": "java.lang.String", "sourceType": "com.alibaba.cloud.ai.autoconfigure.memory.TablestoreChatMemoryProperties", "defaultValue": "session" + }, + { + "name": "spring.ai.alibaba.mem0.client.base-url", + "type": "java.lang.String", + "description": "Base URL for the Mem0 client service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Client", + "defaultValue": "http://localhost:8888" + }, + { + "name": "spring.ai.alibaba.mem0.client.timeout-seconds", + "type": "java.lang.Integer", + "description": "Timeout in seconds for client operations.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Client", + "defaultValue": 30 + }, + { + "name": "spring.ai.alibaba.mem0.client.max-retry-attempts", + "type": "java.lang.Integer", + "description": "Maximum number of retry attempts for failed operations.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Client", + "defaultValue": 3 + }, + { + "name": "spring.ai.alibaba.mem0.server.version", + "type": "java.lang.String", + "description": "Version of the Mem0 server to connect to.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server"}, + { + "name": "spring.ai.alibaba.mem0.server.vector-store.provider", + "type": "java.lang.String", + "description": "Provider for the vector store (e.g., qdrant, chroma, pgvector, pinecone, mongodb, milvus, baidu, upstash_vector, azure_ai_search, redis, elasticsearch, vertex_ai_vector_search, opensearch, supabase, weaviate, faiss, langchain).", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.vector-store.config", + "type": "java.util.Map", + "description": "Configuration map for the vector store provider.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.provider", + "type": "java.lang.String", + "description": "Provider for the graph store.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.config.url", + "type": "java.lang.String", + "description": "URL for the graph store connection.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.config.username", + "type": "java.lang.String", + "description": "Username for the graph store authentication.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.config.password", + "type": "java.lang.String", + "description": "Password for the graph store authentication.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.config.database", + "type": "java.lang.String", + "description": "Database name for Neo4j graph store.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.config.base-label", + "type": "java.lang.Boolean", + "description": "Base label setting for Neo4j graph store.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.graph-store.custom-prompt", + "type": "java.lang.String", + "description": "Custom prompt template for graph store operations.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.llm.provider", + "type": "java.lang.String", + "description": "Provider for the LLM service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.llm.config.api-key", + "type": "java.lang.String", + "description": "API key for the LLM service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.llm.config.temperature", + "type": "java.lang.Double", + "description": "Temperature setting for LLM responses.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.llm.config.model", + "type": "java.lang.String", + "description": "Model name for the LLM service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.llm.config.openai-base-url", + "type": "java.lang.String", + "description": "Base URL for OpenAI-compatible services.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.embedder.provider", + "type": "java.lang.String", + "description": "Provider for the embedding service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.embedder.config.api-key", + "type": "java.lang.String", + "description": "API key for the embedding service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.embedder.config.model", + "type": "java.lang.String", + "description": "Model name for the embedding service.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.embedder.config.openai-base-url", + "type": "java.lang.String", + "description": "Base URL for OpenAI-compatible embedding services.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.history-db-path", + "type": "java.lang.String", + "description": "Path to the history database.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.project.custom-categories", + "type": "java.lang.String", + "description": "Custom categories for the project.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.project.custom-instructions", + "type": "java.lang.String", + "description": "Custom instructions for the project.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.custom-fact-extraction-prompt", + "type": "java.lang.String", + "description": "Custom prompt for fact extraction.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" + }, + { + "name": "spring.ai.alibaba.mem0.server.custom-update-memory-prompt", + "type": "java.lang.String", + "description": "Custom prompt for memory updates.", + "sourceType": "com.alibaba.cloud.ai.memory.mem0.core.Mem0Server" } ], "hints": [] diff --git a/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 386e5e3b50..1027e67adb 100644 --- a/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/auto-configurations/spring-ai-alibaba-autoconfigure-memory/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -27,4 +27,4 @@ com.alibaba.cloud.ai.autoconfigure.memory.ElasticsearchChatMemoryAutoConfigurati com.alibaba.cloud.ai.autoconfigure.memory.MongoDBChatMemoryAutoConfiguration com.alibaba.cloud.ai.autoconfigure.memory.TablestoreChatMemoryAutoConfiguration com.alibaba.cloud.ai.autoconfigure.memory.MemcachedChatMemoryAutoConfiguration - +com.alibaba.cloud.ai.autoconfigure.memory.Mem0ChatMemoryAutoConfiguration diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/pom.xml b/community/memories/spring-ai-alibaba-starter-memory-mem0/pom.xml index dd185ec110..1c4015f28e 100644 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/pom.xml +++ b/community/memories/spring-ai-alibaba-starter-memory-mem0/pom.xml @@ -61,12 +61,6 @@ spring-webflux - - com.alibaba.cloud.ai - spring-ai-alibaba-starter-memory - ${project.version} - - org.springframework.ai spring-ai-advisors-vector-store diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryProperties.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryProperties.java deleted file mode 100644 index 0d8454a4c0..0000000000 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryProperties.java +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.cloud.ai.memory.mem0.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import java.util.HashMap; -import java.util.Map; - -@ConfigurationProperties(prefix = Mem0ChatMemoryProperties.MEM0_PREFIX) -public class Mem0ChatMemoryProperties { - - public static final String MEM0_PREFIX = "spring.ai.alibaba.mem0"; - - private Client client; - private Server server; - - private Mem0ChatMemoryProperties(Builder builder) { - this.client = builder.client; - this.server = builder.server; - } - - public Client getClient() { - return client; - } - - public Server getServer() { - return server; - } - - public static class Builder { - private Client client; - private Server server; - - public Builder client(Client client) { - this.client = client; - return this; - } - - public Builder server(Server server) { - this.server = server; - return this; - } - - public Mem0ChatMemoryProperties build() { - return new Mem0ChatMemoryProperties(this); - } - } - - public static class Client { - private String baseUrl; - private boolean enableCache; - private int timeoutSeconds; - private int maxRetryAttempts; - - private Client(Builder builder) { - this.baseUrl = builder.baseUrl; - this.enableCache = builder.enableCache; - this.timeoutSeconds = builder.timeoutSeconds; - this.maxRetryAttempts = builder.maxRetryAttempts; - } - - public String getBaseUrl() { - return baseUrl; - } - - public boolean isEnableCache() { - return enableCache; - } - - public int getTimeoutSeconds() { - return timeoutSeconds; - } - - public int getMaxRetryAttempts() { - return maxRetryAttempts; - } - - public static class Builder { - private String baseUrl = "http://localhost:8888"; - private boolean enableCache = true; - private int timeoutSeconds = 30; - private int maxRetryAttempts = 3; - - public Builder baseUrl(String baseUrl) { - this.baseUrl = baseUrl; - return this; - } - - public Builder enableCache(boolean enableCache) { - this.enableCache = enableCache; - return this; - } - - public Builder timeoutSeconds(int timeoutSeconds) { - this.timeoutSeconds = timeoutSeconds; - return this; - } - - public Builder maxRetryAttempts(int maxRetryAttempts) { - this.maxRetryAttempts = maxRetryAttempts; - return this; - } - - public Client build() { - return new Client(this); - } - } - } - - public static class Server { - private String version; - private VectorStore vectorStore; - private GraphStore graphStore; - private Llm llm; - private Embedder embedder; - private String historyDbPath; - private Project project; - private String customFactExtractionPrompt; - private String customUpdateMemoryPrompt; - - private Server(Builder builder) { - this.version = builder.version; - this.vectorStore = builder.vectorStore; - this.graphStore = builder.graphStore; - this.llm = builder.llm; - this.embedder = builder.embedder; - this.historyDbPath = builder.historyDbPath; - this.project = builder.project; - this.customFactExtractionPrompt = builder.customFactExtractionPrompt; - this.customUpdateMemoryPrompt = builder.customUpdateMemoryPrompt; - } - - public String getVersion() { - return version; - } - - public VectorStore getVectorStore() { - return vectorStore; - } - - public GraphStore getGraphStore() { - return graphStore; - } - - public Llm getLlm() { - return llm; - } - - public Embedder getEmbedder() { - return embedder; - } - - public String getHistoryDbPath() { - return historyDbPath; - } - - public Project getProject() { - return project; - } - - public String getCustomFactExtractionPrompt() { - return customFactExtractionPrompt; - } - - public String getCustomUpdateMemoryPrompt() { - return customUpdateMemoryPrompt; - } - - public void setCustomFactExtractionPrompt(String prompt) { - this.customFactExtractionPrompt = prompt; - } - - public void setCustomUpdateMemoryPrompt(String prompt) { - this.customUpdateMemoryPrompt = prompt; - } - - public static class Builder { - private String version; - private VectorStore vectorStore; - private GraphStore graphStore; - private Llm llm; - private Embedder embedder; - private String historyDbPath; - private Project project; - private String customFactExtractionPrompt; - private String customUpdateMemoryPrompt; - - public Builder version(String version) { - this.version = version; - return this; - } - - public Builder vectorStore(VectorStore vectorStore) { - this.vectorStore = vectorStore; - return this; - } - - public Builder graphStore(GraphStore graphStore) { - this.graphStore = graphStore; - return this; - } - - public Builder llm(Llm llm) { - this.llm = llm; - return this; - } - - public Builder embedder(Embedder embedder) { - this.embedder = embedder; - return this; - } - - public Builder historyDbPath(String historyDbPath) { - this.historyDbPath = historyDbPath; - return this; - } - - public Builder project(Project project) { - this.project = project; - return this; - } - - public Builder customFactExtractionPrompt(String prompt) { - this.customFactExtractionPrompt = prompt; - return this; - } - - public Builder customUpdateMemoryPrompt(String prompt) { - this.customUpdateMemoryPrompt = prompt; - return this; - } - - public Server build() { - return new Server(this); - } - } - - public static class Project { - private String customCategories; - private String customInstructions; - - private Project(Builder builder) { - this.customCategories = builder.customCategories; - this.customInstructions = builder.customInstructions; - } - - public String getCustomCategories() { - return customCategories; - } - - public String getCustomInstructions() { - return customInstructions; - } - - public void setCustomCategories(String customCategories) { - this.customCategories = customCategories; - } - - public void setCustomInstructions(String customInstructions) { - this.customInstructions = customInstructions; - } - - public static class Builder { - private String customCategories; - private String customInstructions; - - public Builder customCategories(String customCategories) { - this.customCategories = customCategories; - return this; - } - - public Builder customInstructions(String customInstructions) { - this.customInstructions = customInstructions; - return this; - } - - public Project build() { - return new Project(this); - } - } - - } - - public static class VectorStore { - - private String provider; - - /** - * The following vector databases are supported. For specific configurations, - * please refer to the official documentation or Mem0 source code. "qdrant": - * "QdrantConfig", "chroma": "ChromaDbConfig", "pgvector": "PGVectorConfig", - * "pinecone": "PineconeConfig", "mongodb": "MongoDBConfig", "milvus": - * "MilvusDBConfig", "baidu": "BaiduDBConfig", "upstash_vector": - * "UpstashVectorConfig", "azure_ai_search": "AzureAISearchConfig", "redis": - * "RedisDBConfig", "elasticsearch": "ElasticsearchConfig", - * "vertex_ai_vector_search": "GoogleMatchingEngineConfig", "opensearch": - * "OpenSearchConfig", "supabase": "SupabaseConfig", "weaviate": - * "WeaviateConfig", "faiss": "FAISSConfig", "langchain": "LangchainConfig", - */ - private Map config; - - private VectorStore(Builder builder) { - this.provider = builder.provider; - this.config = builder.config; - } - - public String getProvider() { - return provider; - } - - public Map getConfig() { - Map result = new HashMap<>(); - for (Map.Entry entry : config.entrySet()) { - String key = entry.getKey().replace("-", "_"); - result.put(key, entry.getValue()); - } - return result; - } - - public static class Builder { - private String provider; - private Map config = new HashMap<>(); - - public Builder provider(String provider) { - this.provider = provider; - return this; - } - - public Builder config(Map config) { - if (config != null) { - this.config = new HashMap<>(config); - } - return this; - } - - public VectorStore build() { - return new VectorStore(this); - } - } - } - - public static class GraphStore { - private String provider; - private GraphStoreConfig config; - private Llm llm; - /* - * customPrompt: classpath:/prompts/system-message.st - */ - private String customPrompt; - - private GraphStore(Builder builder) { - this.provider = builder.provider; - this.config = builder.config; - this.llm = builder.llm; - this.customPrompt = builder.customPrompt; - } - - public String getProvider() { - return provider; - } - - public GraphStoreConfig getConfig() { - return config; - } - - public Llm getLlm() { - return llm; - } - - public String getCustomPrompt() { - return customPrompt; - } - - public void setCustomPrompt(String customPrompt) { - this.customPrompt = customPrompt; - } - - public static class Builder { - private String provider; - private GraphStoreConfig config; - private Llm llm; - private String customPrompt; - - public Builder provider(String provider) { - this.provider = provider; - return this; - } - - public Builder config(GraphStoreConfig config) { - this.config = config; - return this; - } - - public Builder llm(Llm llm) { - this.llm = llm; - return this; - } - - public Builder customPrompt(String customPrompt) { - this.customPrompt = customPrompt; - return this; - } - - public GraphStore build() { - return new GraphStore(this); - } - } - - public static class GraphStoreConfig { - - private String url; - - private String username; - - private String password; - - // Neo4j supports the following two items: - private String database; - - private Boolean baseLabel; - - private GraphStoreConfig(Builder builder) { - this.url = builder.url; - this.username = builder.username; - this.password = builder.password; - this.database = builder.database; - this.baseLabel = builder.baseLabel; - } - - public String getUrl() { - return url; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public String getDatabase() { - return database; - } - - public Boolean getBaseLabel() { - return baseLabel; - } - - public static class Builder { - private String url; - private String username; - private String password; - private String database; - private Boolean baseLabel; - - public Builder url(String url) { - this.url = url; - return this; - } - - public Builder username(String username) { - this.username = username; - return this; - } - - public Builder password(String password) { - this.password = password; - return this; - } - - public Builder database(String database) { - this.database = database; - return this; - } - - public Builder baseLabel(Boolean baseLabel) { - this.baseLabel = baseLabel; - return this; - } - - public GraphStoreConfig build() { - return new GraphStoreConfig(this); - } - } - } - } - - public static class Llm { - private String provider; - private LlmConfig config; - - private Llm(Builder builder) { - this.provider = builder.provider; - this.config = builder.config; - } - - public String getProvider() { - return provider; - } - - public LlmConfig getConfig() { - return config; - } - - public static class Builder { - private String provider; - private LlmConfig config; - - public Builder provider(String provider) { - this.provider = provider; - return this; - } - - public Builder config(LlmConfig config) { - this.config = config; - return this; - } - - public Llm build() { - return new Llm(this); - } - } - - public static class LlmConfig { - private String apiKey; - private double temperature; - private String model; - private String openaiBaseUrl; - - private LlmConfig(Builder builder) { - this.apiKey = builder.apiKey; - this.temperature = builder.temperature; - this.model = builder.model; - this.openaiBaseUrl = builder.openaiBaseUrl; - } - - public String getApiKey() { - return apiKey; - } - - public double getTemperature() { - return temperature; - } - - public String getModel() { - return model; - } - - public String getOpenaiBaseUrl() { - return openaiBaseUrl; - } - - public static class Builder { - private String apiKey; - private double temperature = 0.7; - private String model = ""; - private String openaiBaseUrl = ""; - - public Builder apiKey(String apiKey) { - this.apiKey = apiKey; - return this; - } - - public Builder temperature(double temperature) { - this.temperature = temperature; - return this; - } - - public Builder model(String model) { - this.model = model; - return this; - } - - public Builder openaiBaseUrl(String openaiBaseUrl) { - this.openaiBaseUrl = openaiBaseUrl; - return this; - } - - public LlmConfig build() { - return new LlmConfig(this); - } - } - } - } - - public static class Embedder { - private String provider; - private EmbedderConfig config; - - private Embedder(Builder builder) { - this.provider = builder.provider; - this.config = builder.config; - } - - public String getProvider() { - return provider; - } - - public EmbedderConfig getConfig() { - return config; - } - - public static class Builder { - private String provider; - private EmbedderConfig config; - - public Builder provider(String provider) { - this.provider = provider; - return this; - } - - public Builder config(EmbedderConfig config) { - this.config = config; - return this; - } - - public Embedder build() { - return new Embedder(this); - } - } - - public static class EmbedderConfig { - private String apiKey; - private String model; - private String openaiBaseUrl; - - private EmbedderConfig(Builder builder) { - this.apiKey = builder.apiKey; - this.model = builder.model; - this.openaiBaseUrl = builder.openaiBaseUrl; - } - - public String getApiKey() { - return apiKey; - } - - public String getModel() { - return model; - } - - public String getOpenaiBaseUrl() { - return openaiBaseUrl; - } - - public static class Builder { - private String apiKey; - private String model; - private String openaiBaseUrl; - - public Builder apiKey(String apiKey) { - this.apiKey = apiKey; - return this; - } - - public Builder model(String model) { - this.model = model; - return this; - } - - public Builder openaiBaseUrl(String openaiBaseUrl) { - this.openaiBaseUrl = openaiBaseUrl; - return this; - } - - public EmbedderConfig build() { - return new EmbedderConfig(this); - } - } - } - } - } -} diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0Client.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0Client.java new file mode 100644 index 0000000000..d4914863eb --- /dev/null +++ b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0Client.java @@ -0,0 +1,120 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.cloud.ai.memory.mem0.core; + +/** + * @author yingzi + * @since 2025/9/14 + */ + +public class Mem0Client { + + private String baseUrl = "http://localhost:8888"; + + private boolean enableCache = true; + + private int timeoutSeconds = 30; + + private int maxRetryAttempts = 3; + + // 私有构造函数,防止直接实例化 + private Mem0Client() { + } + + // 私有构造函数,用于从 Builder 创建实例 + private Mem0Client(Builder builder) { + this.baseUrl = builder.baseUrl; + this.enableCache = builder.enableCache; + this.timeoutSeconds = builder.timeoutSeconds; + this.maxRetryAttempts = builder.maxRetryAttempts; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String baseUrl = "http://localhost:8888"; + + private boolean enableCache = true; + + private int timeoutSeconds = 30; + + private int maxRetryAttempts = 3; + + private Builder() { + } + + public Builder baseUrl(String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + public Builder enableCache(boolean enableCache) { + this.enableCache = enableCache; + return this; + } + + public Builder timeoutSeconds(int timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + return this; + } + + public Builder maxRetryAttempts(int maxRetryAttempts) { + this.maxRetryAttempts = maxRetryAttempts; + return this; + } + + public Mem0Client build() { + return new Mem0Client(this); + } + + } + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public boolean isEnableCache() { + return enableCache; + } + + public void setEnableCache(boolean enableCache) { + this.enableCache = enableCache; + } + + public int getTimeoutSeconds() { + return timeoutSeconds; + } + + public void setTimeoutSeconds(int timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + } + + public int getMaxRetryAttempts() { + return maxRetryAttempts; + } + + public void setMaxRetryAttempts(int maxRetryAttempts) { + this.maxRetryAttempts = maxRetryAttempts; + } + +} diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0Server.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0Server.java new file mode 100644 index 0000000000..586cdcb6bb --- /dev/null +++ b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0Server.java @@ -0,0 +1,833 @@ +/* + * Copyright 2024-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.cloud.ai.memory.mem0.core; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author yingzi + * @since 2025/9/14 + */ + +public class Mem0Server { + + private String version; + + private VectorStore vectorStore; + + private GraphStore graphStore; + + private Llm llm; + + private Embedder embedder; + + private String historyDbPath; + + private Project project; + + private String customFactExtractionPrompt; + + private String customUpdateMemoryPrompt; + + // 私有构造函数,防止直接实例化 + private Mem0Server() { + } + + private Mem0Server(Mem0Server server) { + this.version = server.version; + this.vectorStore = server.vectorStore; + this.graphStore = server.graphStore; + this.llm = server.llm; + this.embedder = server.embedder; + this.historyDbPath = server.historyDbPath; + this.project = server.project; + this.customFactExtractionPrompt = server.customFactExtractionPrompt; + this.customUpdateMemoryPrompt = server.customUpdateMemoryPrompt; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final Mem0Server server = new Mem0Server(); + + private Builder() { + } + + public Builder version(String version) { + server.version = version; + return this; + } + + public Builder vectorStore(VectorStore vectorStore) { + server.vectorStore = vectorStore; + return this; + } + + public Builder graphStore(GraphStore graphStore) { + server.graphStore = graphStore; + return this; + } + + public Builder llm(Llm llm) { + server.llm = llm; + return this; + } + + public Builder embedder(Embedder embedder) { + server.embedder = embedder; + return this; + } + + public Builder historyDbPath(String historyDbPath) { + server.historyDbPath = historyDbPath; + return this; + } + + public Builder project(Project project) { + server.project = project; + return this; + } + + public Builder customFactExtractionPrompt(String customFactExtractionPrompt) { + server.customFactExtractionPrompt = customFactExtractionPrompt; + return this; + } + + public Builder customUpdateMemoryPrompt(String customUpdateMemoryPrompt) { + server.customUpdateMemoryPrompt = customUpdateMemoryPrompt; + return this; + } + + public Mem0Server build() { + return new Mem0Server(server); + } + + } + + public static class Project { + + private String customCategories; + + private String customInstructions; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final Project project = new Project(); + + private Builder() { + } + + public Builder customCategories(String customCategories) { + project.customCategories = customCategories; + return this; + } + + public Builder customInstructions(String customInstructions) { + project.customInstructions = customInstructions; + return this; + } + + public Project build() { + return new Project(project); + } + + } + + // 私有构造函数,防止直接实例化 + private Project() { + } + + // 私有构造函数,用于复制现有实例 + private Project(Project project) { + this.customCategories = project.customCategories; + this.customInstructions = project.customInstructions; + } + + public String getCustomCategories() { + return customCategories; + } + + public void setCustomCategories(String customCategories) { + this.customCategories = customCategories; + } + + public String getCustomInstructions() { + return customInstructions; + } + + public void setCustomInstructions(String customInstructions) { + this.customInstructions = customInstructions; + } + + } + + public static class VectorStore { + + private String provider; + + /** + * The following vector databases are supported. For specific configurations, + * please refer to the official documentation or Mem0 source code. "qdrant": + * "QdrantConfig", "chroma": "ChromaDbConfig", "pgvector": "PGVectorConfig", + * "pinecone": "PineconeConfig", "mongodb": "MongoDBConfig", "milvus": + * "MilvusDBConfig", "baidu": "BaiduDBConfig", "upstash_vector": + * "UpstashVectorConfig", "azure_ai_search": "AzureAISearchConfig", "redis": + * "RedisDBConfig", "elasticsearch": "ElasticsearchConfig", + * "vertex_ai_vector_search": "GoogleMatchingEngineConfig", "opensearch": + * "OpenSearchConfig", "supabase": "SupabaseConfig", "weaviate": "WeaviateConfig", + * "faiss": "FAISSConfig", "langchain": "LangchainConfig", + */ + private Map config; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final VectorStore vectorStore = new VectorStore(); + + private Builder() { + } + + public Builder provider(String provider) { + vectorStore.provider = provider; + return this; + } + + public Builder config(Map config) { + vectorStore.config = config; + return this; + } + + public VectorStore build() { + return new VectorStore(vectorStore); + } + + } + + // 私有构造函数,防止直接实例化 + private VectorStore() { + } + + // 私有构造函数,用于复制现有实例 + private VectorStore(VectorStore vectorStore) { + this.provider = vectorStore.provider; + this.config = vectorStore.config; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public Map getConfig() { + Map result = new HashMap<>(); + for (Map.Entry entry : config.entrySet()) { + String key = entry.getKey().replace("-", "_"); + result.put(key, entry.getValue()); + } + return result; + } + + public void setConfig(Map config) { + this.config = config; + } + + } + + public static class GraphStore { + + private String provider; + + private GraphStoreConfig config; + + private Llm llm; + + /* + * customPrompt: classpath:/prompts/system-message.st + */ + private String customPrompt; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final GraphStore graphStore = new GraphStore(); + + private Builder() { + } + + public Builder provider(String provider) { + graphStore.provider = provider; + return this; + } + + public Builder config(GraphStoreConfig config) { + graphStore.config = config; + return this; + } + + public Builder llm(Llm llm) { + graphStore.llm = llm; + return this; + } + + public Builder customPrompt(String customPrompt) { + graphStore.customPrompt = customPrompt; + return this; + } + + public GraphStore build() { + return new GraphStore(graphStore); + } + + } + + // 私有构造函数,防止直接实例化 + private GraphStore() { + } + + // 私有构造函数,用于复制现有实例 + private GraphStore(GraphStore graphStore) { + this.provider = graphStore.provider; + this.config = graphStore.config; + this.llm = graphStore.llm; + this.customPrompt = graphStore.customPrompt; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public GraphStoreConfig getConfig() { + return config; + } + + public void setConfig(GraphStoreConfig config) { + this.config = config; + } + + public Llm getLlm() { + return llm; + } + + public void setLlm(Llm llm) { + this.llm = llm; + } + + public String getCustomPrompt() { + return customPrompt; + } + + public void setCustomPrompt(String customPrompt) { + this.customPrompt = customPrompt; + } + + public static class GraphStoreConfig { + + private String url; + + private String username; + + private String password; + + // Neo4j supports the following two items: + private String database; + + private Boolean baseLabel; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final GraphStoreConfig config = new GraphStoreConfig(); + + private Builder() { + } + + public Builder url(String url) { + config.url = url; + return this; + } + + public Builder username(String username) { + config.username = username; + return this; + } + + public Builder password(String password) { + config.password = password; + return this; + } + + public Builder database(String database) { + config.database = database; + return this; + } + + public Builder baseLabel(Boolean baseLabel) { + config.baseLabel = baseLabel; + return this; + } + + public GraphStoreConfig build() { + return new GraphStoreConfig(config); + } + + } + + // 私有构造函数,防止直接实例化 + private GraphStoreConfig() { + } + + // 私有构造函数,用于复制现有实例 + private GraphStoreConfig(GraphStoreConfig config) { + this.url = config.url; + this.username = config.username; + this.password = config.password; + this.database = config.database; + this.baseLabel = config.baseLabel; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public Boolean getBaseLabel() { + return baseLabel; + } + + public void setBaseLabel(Boolean baseLabel) { + this.baseLabel = baseLabel; + } + + } + + } + + public static class Llm { + + private String provider; + + private LlmConfig config; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Llm llm = new Llm(); + + private Builder() { + } + + public Builder provider(String provider) { + llm.provider = provider; + return this; + } + + public Builder config(LlmConfig config) { + llm.config = config; + return this; + } + + public Llm build() { + return new Llm(llm); + } + + } + + // 私有构造函数,防止直接实例化 + private Llm() { + } + + // 私有构造函数,用于复制现有实例 + private Llm(Llm llm) { + this.provider = llm.provider; + this.config = llm.config; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public LlmConfig getConfig() { + return config; + } + + public void setConfig(LlmConfig config) { + this.config = config; + } + + public static class LlmConfig { + + private String apiKey; + + private double temperature; + + private String model; + + private String openaiBaseUrl; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final LlmConfig config = new LlmConfig(); + + private Builder() { + } + + public Builder apiKey(String apiKey) { + config.apiKey = apiKey; + return this; + } + + public Builder temperature(double temperature) { + config.temperature = temperature; + return this; + } + + public Builder model(String model) { + config.model = model; + return this; + } + + public Builder openaiBaseUrl(String openaiBaseUrl) { + config.openaiBaseUrl = openaiBaseUrl; + return this; + } + + public LlmConfig build() { + return new LlmConfig(config); + } + + } + + // 私有构造函数,防止直接实例化 + private LlmConfig() { + } + + // 私有构造函数,用于复制现有实例 + private LlmConfig(LlmConfig config) { + this.apiKey = config.apiKey; + this.temperature = config.temperature; + this.model = config.model; + this.openaiBaseUrl = config.openaiBaseUrl; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public double getTemperature() { + return temperature; + } + + public void setTemperature(double temperature) { + this.temperature = temperature; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getOpenaiBaseUrl() { + return openaiBaseUrl; + } + + public void setOpenaiBaseUrl(String openaiBaseUrl) { + this.openaiBaseUrl = openaiBaseUrl; + } + + } + + } + + public static class Embedder { + + private String provider; + + private EmbedderConfig config; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final Embedder embedder = new Embedder(); + + private Builder() { + } + + public Builder provider(String provider) { + embedder.provider = provider; + return this; + } + + public Builder config(EmbedderConfig config) { + embedder.config = config; + return this; + } + + public Embedder build() { + return new Embedder(embedder); + } + + } + + // 私有构造函数,防止直接实例化 + private Embedder() { + } + + // 私有构造函数,用于复制现有实例 + private Embedder(Embedder embedder) { + this.provider = embedder.provider; + this.config = embedder.config; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public EmbedderConfig getConfig() { + return config; + } + + public void setConfig(EmbedderConfig config) { + this.config = config; + } + + public static class EmbedderConfig { + + private String apiKey; + + private String model; + + private String openaiBaseUrl; + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final EmbedderConfig config = new EmbedderConfig(); + + private Builder() { + } + + public Builder apiKey(String apiKey) { + config.apiKey = apiKey; + return this; + } + + public Builder model(String model) { + config.model = model; + return this; + } + + public Builder openaiBaseUrl(String openaiBaseUrl) { + config.openaiBaseUrl = openaiBaseUrl; + return this; + } + + public EmbedderConfig build() { + return new EmbedderConfig(config); + } + + } + + // 私有构造函数,防止直接实例化 + private EmbedderConfig() { + } + + // 私有构造函数,用于复制现有实例 + private EmbedderConfig(EmbedderConfig config) { + this.apiKey = config.apiKey; + this.model = config.model; + this.openaiBaseUrl = config.openaiBaseUrl; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getOpenaiBaseUrl() { + return openaiBaseUrl; + } + + public void setOpenaiBaseUrl(String openaiBaseUrl) { + this.openaiBaseUrl = openaiBaseUrl; + } + + } + + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public VectorStore getVectorStore() { + return vectorStore; + } + + public void setVectorStore(VectorStore vectorStore) { + this.vectorStore = vectorStore; + } + + public GraphStore getGraphStore() { + return graphStore; + } + + public void setGraphStore(GraphStore graphStore) { + this.graphStore = graphStore; + } + + public Llm getLlm() { + return llm; + } + + public void setLlm(Llm llm) { + this.llm = llm; + } + + public Embedder getEmbedder() { + return embedder; + } + + public void setEmbedder(Embedder embedder) { + this.embedder = embedder; + } + + public String getHistoryDbPath() { + return historyDbPath; + } + + public void setHistoryDbPath(String historyDbPath) { + this.historyDbPath = historyDbPath; + } + + public Project getProject() { + return project; + } + + public void setProject(Project project) { + this.project = project; + } + + public String getCustomFactExtractionPrompt() { + return customFactExtractionPrompt; + } + + public void setCustomFactExtractionPrompt(String customFactExtractionPrompt) { + this.customFactExtractionPrompt = customFactExtractionPrompt; + } + + public String getCustomUpdateMemoryPrompt() { + return customUpdateMemoryPrompt; + } + + public void setCustomUpdateMemoryPrompt(String customUpdateMemoryPrompt) { + this.customUpdateMemoryPrompt = customUpdateMemoryPrompt; + } + +} diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClient.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClient.java index 80eb34f91c..b23d0b66f9 100644 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClient.java +++ b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClient.java @@ -15,7 +15,6 @@ */ package com.alibaba.cloud.ai.memory.mem0.core; -import com.alibaba.cloud.ai.memory.mem0.config.Mem0ChatMemoryProperties; import com.alibaba.cloud.ai.memory.mem0.model.Mem0ServerRequest; import com.alibaba.cloud.ai.memory.mem0.model.Mem0ServerResp; import com.fasterxml.jackson.core.JsonProcessingException; @@ -50,7 +49,9 @@ public class Mem0ServiceClient { private final ObjectMapper objectMapper; - private final Mem0ChatMemoryProperties config; + private final Mem0Client mem0Client; + + private final Mem0Server mem0Server; private final ResourceLoader resourceLoader; @@ -66,8 +67,9 @@ public class Mem0ServiceClient { /** * Constructor */ - public Mem0ServiceClient(Mem0ChatMemoryProperties config, ResourceLoader resourceLoader) { - this.config = config; + public Mem0ServiceClient(Mem0Client mem0Client, Mem0Server mem0Server, ResourceLoader resourceLoader) { + this.mem0Client = mem0Client; + this.mem0Server = mem0Server; this.resourceLoader = resourceLoader; this.objectMapper = new ObjectMapper(); // JSON key serialization using snake_case @@ -78,7 +80,7 @@ public Mem0ServiceClient(Mem0ChatMemoryProperties config, ResourceLoader resourc // Create WebClient to connect to Mem0 API this.webClient = WebClient.builder() - .baseUrl(config.getClient().getBaseUrl()) + .baseUrl(mem0Client.getBaseUrl()) .defaultHeader("Content-Type", "application/json") .build(); } @@ -86,26 +88,29 @@ public Mem0ServiceClient(Mem0ChatMemoryProperties config, ResourceLoader resourc /** * Configures Mem0 */ - public void configure(Mem0ChatMemoryProperties.Server config) { + public void configure(Mem0Server mem0server) { try { - if (Objects.nonNull(config.getProject())) { - config.getProject().setCustomInstructions(this.loadPrompt(config.getProject().getCustomInstructions())); - config.getProject().setCustomCategories(this.loadPrompt(config.getProject().getCustomCategories())); + if (Objects.nonNull(mem0server.getProject())) { + mem0server.getProject() + .setCustomInstructions(this.loadPrompt(mem0server.getProject().getCustomInstructions())); + mem0server.getProject() + .setCustomCategories(this.loadPrompt(mem0server.getProject().getCustomCategories())); } - if (Objects.nonNull(config.getVectorStore())) { - config.getGraphStore().setCustomPrompt(this.loadPrompt(config.getGraphStore().getCustomPrompt())); + if (Objects.nonNull(mem0server.getVectorStore())) { + mem0server.getGraphStore() + .setCustomPrompt(this.loadPrompt(mem0server.getGraphStore().getCustomPrompt())); } - config.setCustomFactExtractionPrompt(this.loadPrompt(config.getCustomFactExtractionPrompt())); - config.setCustomUpdateMemoryPrompt(this.loadPrompt(config.getCustomUpdateMemoryPrompt())); + mem0server.setCustomFactExtractionPrompt(this.loadPrompt(mem0server.getCustomFactExtractionPrompt())); + mem0server.setCustomUpdateMemoryPrompt(this.loadPrompt(mem0server.getCustomUpdateMemoryPrompt())); - String requestJson = objectMapper.writeValueAsString(config); + String requestJson = objectMapper.writeValueAsString(mem0server); String response = webClient.post() .uri(CONFIGURE_ENDPOINT) .contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(requestJson)) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(this.config.getClient().getTimeoutSeconds())) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) .block(); if (StringUtils.hasText(response) && response.contains("successfully")) { logger.info("Mem0 configuration updated successfully"); @@ -138,8 +143,8 @@ public void addMemory(Mem0ServerRequest.MemoryCreate memoryCreate) { .body(BodyInserters.fromValue(requestJson)) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) - .retry(config.getClient().getMaxRetryAttempts()) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) + .retry(this.mem0Client.getMaxRetryAttempts()) .block(); if (response != null) { @@ -177,8 +182,8 @@ public Mem0ServerResp getAllMemories(String userId, String runId, String agentId }) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) - .retry(config.getClient().getMaxRetryAttempts()) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) + .retry(this.mem0Client.getMaxRetryAttempts()) .block(); if (response != null) { @@ -204,8 +209,8 @@ public Mem0ServerResp getMemory(String memoryId) { .uri(MEMORIES_ENDPOINT + "/{memoryId}", memoryId) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) - .retry(config.getClient().getMaxRetryAttempts()) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) + .retry(this.mem0Client.getMaxRetryAttempts()) .block(); if (response != null) { @@ -243,8 +248,8 @@ public Mem0ServerResp searchMemories(Mem0ServerRequest.SearchRequest searchReque .body(BodyInserters.fromValue(requestJson)) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) - .retry(config.getClient().getMaxRetryAttempts()) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) + .retry(this.mem0Client.getMaxRetryAttempts()) .block(); if (response != null) { @@ -274,8 +279,8 @@ public Map updateMemory(String memoryId, Map upd .bodyValue(updatedMemory) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) - .retry(config.getClient().getMaxRetryAttempts()) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) + .retry(this.mem0Client.getMaxRetryAttempts()) .block(); if (response != null) { @@ -302,7 +307,7 @@ public List> getMemoryHistory(String memoryId) { .uri(MEMORIES_ENDPOINT + "/{memoryId}/history", memoryId) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) .block(); if (response != null) { @@ -356,7 +361,7 @@ public void deleteMemory(String memoryId) { .uri(MEMORIES_ENDPOINT + "/{memoryId}", memoryId) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) .block(); logger.info("Successfully deleted memory: {}", memoryId); @@ -384,7 +389,7 @@ public void deleteAllMemories(String userId, String runId, String agentId) { }) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) .block(); logger.info("Successfully deleted all memories"); @@ -404,7 +409,7 @@ public void resetAllMemories() { .uri(RESET_ENDPOINT) .retrieve() .bodyToMono(String.class) - .timeout(Duration.ofSeconds(config.getClient().getTimeoutSeconds())) + .timeout(Duration.ofSeconds(this.mem0Client.getTimeoutSeconds())) .block(); logger.info("Successfully reset all memories"); diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 79279bdfa4..0000000000 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -com.alibaba.cloud.ai.memory.mem0.config.Mem0ChatMemoryAutoConfiguration diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryPropertiesTest.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryPropertiesTest.java deleted file mode 100644 index a37d829b85..0000000000 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/config/Mem0ChatMemoryPropertiesTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.cloud.ai.memory.mem0.config; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for Mem0ChatMemoryProperties - * - * @author Morain Miao - * @since 1.0.0 - */ -class Mem0ChatMemoryPropertiesTest { - - @Test - void testDefaultConstructor() { - // When - Mem0ChatMemoryProperties properties = new Mem0ChatMemoryProperties.Builder().build(); - - // Then - assertThat(properties).isNotNull(); - // Note: The client and server fields are null in the default constructor and need - // to be manually initialized. - assertThat(properties.getClient()).isNull(); - assertThat(properties.getServer()).isNull(); - } - - @Test - void testClientProperties() { - // Given - Mem0ChatMemoryProperties.Client client = new Mem0ChatMemoryProperties.Client.Builder().baseUrl("http://localhost:8888").timeoutSeconds(30).build(); - Mem0ChatMemoryProperties properties = new Mem0ChatMemoryProperties.Builder().client(client).build(); - - // Then - assertThat(properties.getClient().getBaseUrl()).isEqualTo("http://localhost:8888"); - assertThat(properties.getClient().getTimeoutSeconds()).isEqualTo(30); - } - - @Test - void testServerProperties() { - // Given - Mem0ChatMemoryProperties.Server server = new Mem0ChatMemoryProperties.Server.Builder().version("v1.1").build(); - Mem0ChatMemoryProperties properties = new Mem0ChatMemoryProperties.Builder().server(server).build(); - - // Then - assertThat(properties.getServer().getVersion()).isEqualTo("v1.1"); - } - - @Test - void testServerCustomPrompts() { - // Given - Mem0ChatMemoryProperties.Server server = new Mem0ChatMemoryProperties.Server.Builder() - .customFactExtractionPrompt("fact extraction prompt") - .customUpdateMemoryPrompt("update memory prompt").build(); - - // Then - assertThat(server.getCustomFactExtractionPrompt()).isEqualTo("fact extraction prompt"); - assertThat(server.getCustomUpdateMemoryPrompt()).isEqualTo("update memory prompt"); - } - - @Test - void testClientDefaultValues() { - // Given - Mem0ChatMemoryProperties.Client client = new Mem0ChatMemoryProperties.Client.Builder().build(); - - // Then - Verify default values - assertThat(client.getBaseUrl()).isEqualTo("http://localhost:8888"); - assertThat(client.getTimeoutSeconds()).isEqualTo(30); - } - - @Test - void testServerDefaultValues() { - // Given - Mem0ChatMemoryProperties.Server server = new Mem0ChatMemoryProperties.Server.Builder().build(); - - // Then - Verify default values - assertThat(server.getVersion()).isNull(); - assertThat(server.getProject()).isNull(); - assertThat(server.getVectorStore()).isNull(); - assertThat(server.getGraphStore()).isNull(); - assertThat(server.getCustomFactExtractionPrompt()).isNull(); - assertThat(server.getCustomUpdateMemoryPrompt()).isNull(); - } - - @Test - void testPropertiesEquality() { - // When - The client object needs to be initialized first - Mem0ChatMemoryProperties.Client client1 = new Mem0ChatMemoryProperties.Client.Builder() - .baseUrl("http://localhost:8888").build(); - Mem0ChatMemoryProperties.Client client2 = new Mem0ChatMemoryProperties.Client.Builder() - .baseUrl("http://localhost:8888").build(); - Mem0ChatMemoryProperties properties1 = new Mem0ChatMemoryProperties.Builder().client(client1).build(); - Mem0ChatMemoryProperties properties2 = new Mem0ChatMemoryProperties.Builder().client(client2).build(); - - // Then - assertThat(properties1.getClient().getBaseUrl()).isEqualTo(properties2.getClient().getBaseUrl()); - } - - @Test - void testPropertiesToString() { - // Given - Mem0ChatMemoryProperties properties = new Mem0ChatMemoryProperties.Builder().build(); - - // When - String toString = properties.toString(); - - // Then - assertThat(toString).isNotNull(); - assertThat(toString).contains("Mem0ChatMemoryProperties"); - } - -} diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClientTest.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClientTest.java index d4339e0514..a67553d652 100644 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClientTest.java +++ b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/Mem0ServiceClientTest.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,6 @@ */ package com.alibaba.cloud.ai.memory.mem0.core; -import com.alibaba.cloud.ai.memory.mem0.config.Mem0ChatMemoryProperties; import com.alibaba.cloud.ai.memory.mem0.model.Mem0ServerRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,10 +28,9 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * Unit tests for Mem0ServiceClient + * Unit tests for Mem0ServiceClientTest * * @author Morain Miao * @since 1.0.0 @@ -43,17 +41,22 @@ class Mem0ServiceClientTest { @Mock private ResourceLoader resourceLoader; - private Mem0ChatMemoryProperties properties; + private Mem0Client mem0Client; + + private Mem0Server mem0Server; private Mem0ServiceClient client; @BeforeEach void setUp() { - Mem0ChatMemoryProperties.Client clientConfig = new Mem0ChatMemoryProperties.Client.Builder() - .baseUrl("http://localhost:8888").timeoutSeconds(30).build(); - properties = new Mem0ChatMemoryProperties.Builder().client(clientConfig).build(); + Mem0Client mem0Client = Mem0Client.builder().baseUrl("http://localhost:8888").enableCache(true).build(); + this.mem0Client = mem0Client; + + Mem0Server mem0Server = Mem0Server.builder().version("v1.1").build(); + ; + this.mem0Server = mem0Server; - client = new Mem0ServiceClient(properties, resourceLoader); + client = new Mem0ServiceClient(mem0Client, mem0Server, resourceLoader); } @Test @@ -61,19 +64,11 @@ void testConstructor() { assertThat(client).isNotNull(); } - @Test - void testConstructorWithNullProperties() { - // Since the constructor lacks null checks, a NullPointerException will be thrown - // here - // Not in the constructor itself, but when the config is subsequently used - assertThatThrownBy(() -> new Mem0ServiceClient(null, resourceLoader)).isInstanceOf(NullPointerException.class); - } - @Test void testConstructorWithNullResourceLoader() { // Since the constructor lacks null checks, no exception will be thrown here // But an exception will be thrown when resourceLoader is subsequently used - Mem0ServiceClient client = new Mem0ServiceClient(properties, null); + Mem0ServiceClient client = new Mem0ServiceClient(this.mem0Client, this.mem0Server, null); assertThat(client).isNotNull(); } @@ -122,16 +117,6 @@ void testSearchMemories() { assertThat(searchRequest.getRunId()).isEqualTo("test-run"); } - @Test - void testConfigure() { - // Given - Mem0ChatMemoryProperties.Server serverConfig = new Mem0ChatMemoryProperties.Server.Builder() - .version("v1.1").build(); - - // When & Then - Verify that the configuration object is created correctly - assertThat(serverConfig.getVersion()).isEqualTo("v1.1"); - } - @Test void testMemoryCreateBuilder() { // Given diff --git a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/TestUtils.java b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/TestUtils.java index 202b5124c5..b693ccde39 100644 --- a/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/TestUtils.java +++ b/community/memories/spring-ai-alibaba-starter-memory-mem0/src/test/java/com/alibaba/cloud/ai/memory/mem0/core/TestUtils.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * https://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,7 +16,6 @@ package com.alibaba.cloud.ai.memory.mem0.core; import com.alibaba.cloud.ai.memory.mem0.advisor.Mem0ChatMemoryAdvisor; -import com.alibaba.cloud.ai.memory.mem0.config.Mem0ChatMemoryProperties; import com.alibaba.cloud.ai.memory.mem0.model.Mem0ServerRequest; import com.alibaba.cloud.ai.memory.mem0.model.Mem0ServerResp; import org.springframework.ai.chat.messages.Message; @@ -36,20 +35,14 @@ */ public class TestUtils { - /** - * Creates test Mem0ChatMemoryProperties for testing - */ - public static Mem0ChatMemoryProperties createTestProperties() { - // Configure client - Mem0ChatMemoryProperties.Client client = new Mem0ChatMemoryProperties.Client.Builder().baseUrl("http://localhost:8888") - .timeoutSeconds(30).build(); - - // Configure server - Mem0ChatMemoryProperties.Server server = new Mem0ChatMemoryProperties.Server.Builder() - .version("v1.1").build(); + public static Mem0Client createTestMem0Client() { + Mem0Client mem0Client = Mem0Client.builder().baseUrl("http://localhost:8888").timeoutSeconds(30).build(); + return mem0Client; + } - Mem0ChatMemoryProperties properties = new Mem0ChatMemoryProperties.Builder().client(client).server(server).build(); - return properties; + public static Mem0Server createTestMem0Server() { + Mem0Server mem0Server = Mem0Server.builder().version("v1.1").build(); + return mem0Server; } /** diff --git a/pom.xml b/pom.xml index b8eccc0a21..3979e88f70 100644 --- a/pom.xml +++ b/pom.xml @@ -196,6 +196,8 @@ spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-nl2sql spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-graph-observation spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-a2a-server + auto-configurations/spring-ai-alibaba-autoconfigure-memory-long + spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory-long spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-a2a-client spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-a2a-registry diff --git a/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/GraphObservationLifecycleListener.java b/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/GraphObservationLifecycleListener.java index 5e41fbaa00..59a7326341 100644 --- a/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/GraphObservationLifecycleListener.java +++ b/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/GraphObservationLifecycleListener.java @@ -108,7 +108,7 @@ public void before(String nodeId, Map state, RunnableConfig conf // Add input state using Documentation constant nodeObservation.highCardinalityKeyValue( - GraphNodeObservationDocumentation.HighCardinalityKeyNames.GEN_AI_PROMPT.asString(), + GraphNodeObservationDocumentation.HighCardinalityKeyNames.NODE_BEFOR_STATE.asString(), state != null ? state.toString() : ""); nodeObservation.start(); @@ -140,7 +140,7 @@ public void after(String nodeId, Map state, RunnableConfig confi if (nodeObservation != null) { // Add output state using Documentation constant nodeObservation.highCardinalityKeyValue( - GraphNodeObservationDocumentation.HighCardinalityKeyNames.GEN_AI_COMPLETION.asString(), + GraphNodeObservationDocumentation.HighCardinalityKeyNames.NODE_AFTER_STATE.asString(), state != null ? state.toString() : ""); nodeObservation.stop(); @@ -172,7 +172,7 @@ public void onError(String nodeId, Map state, Throwable ex, Runn if (nodeObservation != null) { // Add error state using Documentation constant nodeObservation.highCardinalityKeyValue( - GraphNodeObservationDocumentation.HighCardinalityKeyNames.GEN_AI_COMPLETION.asString(), + GraphNodeObservationDocumentation.HighCardinalityKeyNames.NODE_AFTER_STATE.asString(), state != null ? state.toString() : ""); nodeObservation.error(ex).stop(); @@ -193,44 +193,54 @@ public void onError(String nodeId, Map state, Throwable ex, Runn public void onComplete(String nodeId, Map state, RunnableConfig config) { log.debug("Graph execution completed"); - nodeScopes.values().forEach(scope -> { - try { - scope.close(); - } - catch (Exception e) { - log.debug("Error closing node scope: {}", e.getMessage()); - } - }); + // Close all node scopes with proper error handling + nodeScopes.values().forEach(this::safeCloseScope); nodeScopes.clear(); - nodeObservations.values().forEach(observation -> { - try { - observation.stop(); - } - catch (Exception e) { - log.debug("Error stopping node observation: {}", e.getMessage()); - } - }); + // Stop all node observations with proper error handling + nodeObservations.values().forEach(this::safeStopObservation); nodeObservations.clear(); + // Close graph scope with proper error handling if (graphScope != null) { - try { - graphScope.close(); - } - catch (Exception e) { - log.debug("Error closing graph scope: {}", e.getMessage()); - } + safeCloseScope(graphScope); graphScope = null; } + // Stop graph observation with proper error handling if (graphObservation != null) { - try { - graphObservation.stop(); + safeStopObservation(graphObservation); + graphObservation = null; + } + } + + /** + * Safely close an observation scope, catching and logging any exceptions. + * @param scope the scope to close + */ + private void safeCloseScope(Observation.Scope scope) { + try { + if (scope != null) { + scope.close(); } - catch (Exception e) { - log.debug("Error stopping graph observation: {}", e.getMessage()); + } + catch (Exception e) { + log.debug("Error closing observation scope: {}", e.getMessage(), e); + } + } + + /** + * Safely stop an observation, catching and logging any exceptions. + * @param observation the observation to stop + */ + private void safeStopObservation(Observation observation) { + try { + if (observation != null) { + observation.stop(); } - graphObservation = null; + } + catch (Exception e) { + log.debug("Error stopping observation: {}", e.getMessage(), e); } } diff --git a/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/node/GraphNodeObservationDocumentation.java b/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/node/GraphNodeObservationDocumentation.java index 1834773ce8..f2c0736d7d 100644 --- a/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/node/GraphNodeObservationDocumentation.java +++ b/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/observation/node/GraphNodeObservationDocumentation.java @@ -106,10 +106,10 @@ public enum HighCardinalityKeyNames implements KeyName { * convention). Note: This key is dynamically added by * GraphObservationLifecycleListener, not by Convention. */ - GEN_AI_PROMPT { + NODE_BEFOR_STATE { @Override public String asString() { - return "gen_ai.prompt"; + return "node.before.state"; } }, @@ -118,10 +118,10 @@ public String asString() { * semantic convention). Note: This key is dynamically added by * GraphObservationLifecycleListener, not by Convention. */ - GEN_AI_COMPLETION { + NODE_AFTER_STATE { @Override public String asString() { - return "gen_ai.completion"; + return "node.after.state"; } } diff --git a/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory-long/pom.xml b/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory-long/pom.xml new file mode 100644 index 0000000000..a4d7bb5c04 --- /dev/null +++ b/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory-long/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + com.alibaba.cloud.ai + spring-ai-alibaba + ${revision} + ../../pom.xml + + + spring-ai-alibaba-starter-memory-long + + jar + Spring AI Alibaba Starter Long Memory + https://github.com/alibaba/spring-ai-alibaba + + git://github.com/alibaba/spring-ai-alibaba.git + git@github.com:alibaba/spring-ai-alibaba.git + https://github.com/alibaba/spring-ai-alibaba + + + + + com.alibaba.cloud.ai + spring-ai-alibaba-starter-memory-mem0 + ${revision} + + + + diff --git a/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory/pom.xml b/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory/pom.xml index 5b3d3f204c..2ceab5d2e7 100644 --- a/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory/pom.xml +++ b/spring-ai-alibaba-spring-boot-starters/spring-ai-alibaba-starter-memory/pom.xml @@ -77,6 +77,7 @@ ${revision} + org.springframework.boot spring-boot-starter