-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Feature/mongodb extension #2698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
fb83a67
e3b5a67
03fd925
78107bd
8847e93
b4a310d
2696fbe
6fcdea8
bfb1c49
38a95b1
4786856
be32639
1d1047f
4f0d5d3
c806a97
595bea5
c2bc7a6
92c622d
66142ed
995f0f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!-- | ||
| ~ Copyright 2024-2026 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 | ||
| ~ | ||
| ~ http://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. | ||
| --> | ||
|
|
||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>io.agentscope</groupId> | ||
| <artifactId>agentscope-extensions</artifactId> | ||
| <version>${revision}</version> | ||
| <relativePath>../pom.xml</relativePath> | ||
| </parent> | ||
|
|
||
| <name>AgentScope Java - Extensions - MongoDB</name> | ||
| <description>MongoDB-backed distributed implementation for AgentStateStore. Provides MongoDistributedStore for one-line distributed configuration.</description> | ||
| <artifactId>agentscope-extensions-mongodb</artifactId> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>io.agentscope</groupId> | ||
| <artifactId>agentscope-core</artifactId> | ||
| <scope>provided</scope> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>io.agentscope</groupId> | ||
| <artifactId>agentscope-harness</artifactId> | ||
| <scope>provided</scope> | ||
| </dependency> | ||
|
|
||
| <!-- MongoDB Java Driver (sync) --> | ||
| <dependency> | ||
| <groupId>org.mongodb</groupId> | ||
| <artifactId>mongodb-driver-sync</artifactId> | ||
| </dependency> | ||
|
|
||
| <!-- Test dependencies --> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>org.mockito</groupId> | ||
| <artifactId>mockito-core</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>org.mockito</groupId> | ||
| <artifactId>mockito-junit-jupiter</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| /* | ||
| * Copyright 2024-2026 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 | ||
| * | ||
| * http://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 io.agentscope.extensions.mongodb; | ||
|
|
||
| import com.mongodb.ConnectionString; | ||
| import com.mongodb.MongoClientSettings; | ||
| import com.mongodb.client.MongoClient; | ||
| import com.mongodb.client.MongoClients; | ||
| import com.mongodb.client.MongoDatabase; | ||
| import io.agentscope.core.state.AgentStateStore; | ||
| import io.agentscope.extensions.mongodb.sandbox.MongoSandboxExecutionGuard; | ||
| import io.agentscope.extensions.mongodb.snapshot.MongoSnapshotSpec; | ||
| import io.agentscope.extensions.mongodb.state.MongoAgentStateStore; | ||
| import io.agentscope.extensions.mongodb.store.MongoBaseStore; | ||
| import io.agentscope.harness.agent.DistributedStore; | ||
| import io.agentscope.harness.agent.filesystem.remote.store.BaseStore; | ||
| import io.agentscope.harness.agent.sandbox.SandboxExecutionGuard; | ||
| import io.agentscope.harness.agent.sandbox.snapshot.SandboxSnapshotSpec; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * MongoDB-backed {@link DistributedStore}. | ||
| * | ||
| * <p>Usage: | ||
| * | ||
| * <pre>{@code | ||
| * MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017"); | ||
| * | ||
| * HarnessAgent agent = HarnessAgent.builder() | ||
| * .name("my-agent") | ||
| * .model("dashscope:qwen-plus") | ||
| * .distributedStore(MongoDistributedStore.create(mongoClient, "agentscope")) | ||
| * .build(); | ||
| * }</pre> | ||
| * | ||
| * <p>This configures: | ||
| * | ||
| * <ul> | ||
| * <li>{@link MongoAgentStateStore} — agent session state in MongoDB | ||
| * <li>{@link MongoBaseStore} — workspace filesystem KV in MongoDB | ||
| * <li>{@link MongoSandboxExecutionGuard} — sandbox execution locking in MongoDB | ||
| * <li>{@link MongoSnapshotSpec} — sandbox workspace snapshots in MongoDB | ||
| * </ul> | ||
| * | ||
| * <p>When created via {@link #create(MongoClient)}, the caller owns the {@link MongoClient} | ||
| * lifecycle; {@link #close()} will NOT close the client. When created via {@link | ||
| * #fromConnectionString(String)}, the store owns the client and {@link #close()} will close it. | ||
| */ | ||
| public class MongoDistributedStore implements DistributedStore, AutoCloseable { | ||
|
|
||
| private static final String DEFAULT_DATABASE = "agentscope"; | ||
| private static final String STATE_COLLECTION = "agentscope_sessions"; | ||
| private static final String BASE_COLLECTION = "agentscope_base"; | ||
|
|
||
| private final MongoClient mongoClient; | ||
| private final boolean ownsClient; | ||
| private final String databaseName; | ||
|
|
||
| private volatile AgentStateStore cachedAgentStateStore; | ||
| private volatile BaseStore cachedBaseStore; | ||
| private volatile SandboxSnapshotSpec cachedSnapshotSpec; | ||
| private volatile SandboxExecutionGuard cachedExecutionGuard; | ||
|
|
||
| private MongoDistributedStore(MongoClient mongoClient, String databaseName) { | ||
| this(mongoClient, databaseName, false); | ||
| } | ||
|
|
||
| private MongoDistributedStore( | ||
| MongoClient mongoClient, String databaseName, boolean ownsClient) { | ||
| this.mongoClient = Objects.requireNonNull(mongoClient, "mongoClient"); | ||
| this.ownsClient = ownsClient; | ||
| this.databaseName = databaseName != null ? databaseName : DEFAULT_DATABASE; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a MongoDB distributed store with the default database name ({@code "agentscope"}). | ||
| * | ||
| * @param mongoClient the MongoDB client | ||
| * @return a new MongoDB distributed store | ||
| */ | ||
| public static MongoDistributedStore create(MongoClient mongoClient) { | ||
| return new MongoDistributedStore(mongoClient, null); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a MongoDB distributed store. | ||
| * | ||
| * @param mongoClient the MongoDB client | ||
| * @param databaseName the database name | ||
| * @return a new MongoDB distributed store | ||
| */ | ||
| public static MongoDistributedStore create(MongoClient mongoClient, String databaseName) { | ||
| return new MongoDistributedStore(mongoClient, databaseName); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a MongoDB distributed store from a connection string. A new {@link MongoClient} is | ||
| * created internally and owned by the store; {@link #close()} will close it. | ||
| * | ||
| * @param connectionString the MongoDB connection string | ||
| * @return a new MongoDB distributed store | ||
| */ | ||
| public static MongoDistributedStore fromConnectionString(String connectionString) { | ||
| MongoClientSettings settings = | ||
| MongoClientSettings.builder() | ||
| .applyConnectionString(new ConnectionString(connectionString)) | ||
| .build(); | ||
| return new MongoDistributedStore(MongoClients.create(settings), null, true); | ||
| } | ||
|
|
||
| @Override | ||
| public AgentStateStore agentStateStore() { | ||
| AgentStateStore result = cachedAgentStateStore; | ||
| if (result == null) { | ||
| synchronized (this) { | ||
| result = cachedAgentStateStore; | ||
| if (result == null) { | ||
| result = | ||
| MongoAgentStateStore.builder() | ||
| .mongoClient(mongoClient) | ||
| .databaseName(databaseName) | ||
| .collectionName(STATE_COLLECTION) | ||
| .build(); | ||
| cachedAgentStateStore = result; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public BaseStore baseStore() { | ||
| BaseStore result = cachedBaseStore; | ||
| if (result == null) { | ||
| synchronized (this) { | ||
| result = cachedBaseStore; | ||
| if (result == null) { | ||
| MongoDatabase db = mongoClient.getDatabase(databaseName); | ||
| result = new MongoBaseStore(db, BASE_COLLECTION); | ||
| cachedBaseStore = result; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public SandboxSnapshotSpec sandboxSnapshotSpec() { | ||
| SandboxSnapshotSpec result = cachedSnapshotSpec; | ||
| if (result == null) { | ||
| synchronized (this) { | ||
| result = cachedSnapshotSpec; | ||
| if (result == null) { | ||
| result = new MongoSnapshotSpec(mongoClient, databaseName); | ||
| cachedSnapshotSpec = result; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public SandboxExecutionGuard sandboxExecutionGuard() { | ||
| SandboxExecutionGuard result = cachedExecutionGuard; | ||
| if (result == null) { | ||
| synchronized (this) { | ||
| result = cachedExecutionGuard; | ||
| if (result == null) { | ||
| result = | ||
| MongoSandboxExecutionGuard.builder(mongoClient) | ||
| .databaseName(databaseName) | ||
| .build(); | ||
| cachedExecutionGuard = result; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] When Since they share the same |
||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public void close() { | ||
| if (ownsClient) { | ||
| mongoClient.close(); | ||
| } | ||
| } | ||
| } | ||
This comment was marked as abuse.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.