Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradient-labs-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>ai.gradientlabs</groupId>
<artifactId>gradient-labs-parent</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>1.1.0-SNAPSHOT</version>
</parent>

<artifactId>gradient-labs-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,24 @@ public void deleteConversation(String conversationId) {
httpClient.delete(path, Void.class);
}

/**
* Bulk uploads a batch of memories scoped to a conversation.
* <p>
* Memories are stored verbatim as raw JSON payloads for the AI agent to search
* over on demand during the conversation. The request's idempotency key
* de-duplicates retries: re-uploading with the same key returns the original
* upload instead of inserting again.
*
* @param conversationId the conversation ID
* @param request the memories to upload
* @return the upload result (upload ID and number of memories inserted)
* @throws GradientLabsException if the request fails
*/
public ai.gradientlabs.client.response.ConversationMemoriesBulkUploadResponse bulkUploadConversationMemories(String conversationId, ConversationMemoriesBulkUploadRequest request) {
String path = String.format("/conversations/%s/memories", conversationId);
return httpClient.post(path, request, ai.gradientlabs.client.response.ConversationMemoriesBulkUploadResponse.class);
}

// ==================== Tool Operations ====================

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package ai.gradientlabs.client.request;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;
import java.util.Map;

/**
* Parameters for bulk uploading a batch of memories scoped to a conversation.
* <p>
* Memories are stored verbatim as raw JSON payloads for the AI agent to search
* over on demand during the conversation.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ConversationMemoriesBulkUploadRequest {

@JsonProperty("idempotency_key")
private final String idempotencyKey;

@JsonProperty("memories")
private final List<Map<String, Object>> memories;

@JsonProperty("created_at_keys")
private final List<String> createdAtKeys;

private ConversationMemoriesBulkUploadRequest(Builder builder) {
this.idempotencyKey = builder.idempotencyKey;
this.memories = builder.memories;
this.createdAtKeys = builder.createdAtKeys;
}

public static Builder builder() {
return new Builder();
}

public String getIdempotencyKey() {
return idempotencyKey;
}

public List<Map<String, Object>> getMemories() {
return memories;
}

public List<String> getCreatedAtKeys() {
return createdAtKeys;
}

public static class Builder {
private String idempotencyKey;
private List<Map<String, Object>> memories;
private List<String> createdAtKeys;

private Builder() {
}

/**
* Sets the idempotency key (required).
* <p>
* De-duplicates retries of the same upload. Re-uploading with the same key
* returns the original upload instead of inserting again.
*
* @param idempotencyKey the idempotency key
* @return this builder
*/
public Builder idempotencyKey(String idempotencyKey) {
this.idempotencyKey = idempotencyKey;
return this;
}

/**
* Sets the batch of memories to store (required).
* <p>
* Each element is an arbitrary JSON object stored verbatim as the memory's
* raw payload.
*
* @param memories the memories to store
* @return this builder
*/
public Builder memories(List<Map<String, Object>> memories) {
this.memories = memories;
return this;
}

/**
* Sets the JSON keys tried in order to read each memory's timestamp (optional).
* <p>
* The keys are tried in order against each memory's payload. When none match,
* the upload time is used.
*
* @param createdAtKeys the created-at keys
* @return this builder
*/
public Builder createdAtKeys(List<String> createdAtKeys) {
this.createdAtKeys = createdAtKeys;
return this;
}

/**
* Builds the request.
*
* @return a new request instance
* @throws IllegalStateException if required fields are missing
*/
public ConversationMemoriesBulkUploadRequest build() {
if (idempotencyKey == null || idempotencyKey.isBlank()) {
throw new IllegalStateException("idempotencyKey is required");
}
if (memories == null || memories.isEmpty()) {
throw new IllegalStateException("memories is required and cannot be empty");
}
return new ConversationMemoriesBulkUploadRequest(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ai.gradientlabs.client.response;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Response from a conversation memories bulk upload.
*/
public class ConversationMemoriesBulkUploadResponse {

@JsonProperty("upload_id")
private String uploadId;

@JsonProperty("memories_inserted")
private long memoriesInserted;

/**
* Gets the identifier for this upload.
*
* @return the upload ID
*/
public String getUploadId() {
return uploadId;
}

public void setUploadId(String uploadId) {
this.uploadId = uploadId;
}

/**
* Gets the number of memories inserted by this upload.
*
* @return the number of memories inserted
*/
public long getMemoriesInserted() {
return memoriesInserted;
}

public void setMemoriesInserted(long memoriesInserted) {
this.memoriesInserted = memoriesInserted;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package ai.gradientlabs.client.request;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class ConversationMemoriesBulkUploadRequestTest {

private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());

@Test
void serializesAllFields() throws Exception {
ConversationMemoriesBulkUploadRequest request = ConversationMemoriesBulkUploadRequest.builder()
.idempotencyKey("key-123")
.memories(List.of(
Map.of("title", "Order #1", "amount", 42),
Map.of("title", "Order #2")))
.createdAtKeys(List.of("created_at", "timestamp"))
.build();

JsonNode json = objectMapper.valueToTree(request);

assertEquals("key-123", json.get("idempotency_key").asText());
assertTrue(json.get("memories").isArray());
assertEquals(2, json.get("memories").size());
assertEquals("Order #1", json.get("memories").get(0).get("title").asText());
assertEquals(42, json.get("memories").get(0).get("amount").asInt());
assertEquals("created_at", json.get("created_at_keys").get(0).asText());
assertEquals("timestamp", json.get("created_at_keys").get(1).asText());
}

@Test
void omitsCreatedAtKeysWhenNotSet() throws Exception {
ConversationMemoriesBulkUploadRequest request = ConversationMemoriesBulkUploadRequest.builder()
.idempotencyKey("key-123")
.memories(List.of(Map.of("title", "Order #1")))
.build();

JsonNode json = objectMapper.valueToTree(request);

assertFalse(json.has("created_at_keys"));
}

@Test
void requiresIdempotencyKey() {
assertThrows(IllegalStateException.class, () -> ConversationMemoriesBulkUploadRequest.builder()
.memories(List.of(Map.of("title", "Order #1")))
.build());
}

@Test
void requiresNonEmptyMemories() {
assertThrows(IllegalStateException.class, () -> ConversationMemoriesBulkUploadRequest.builder()
.idempotencyKey("key-123")
.memories(List.of())
.build());
}
}
2 changes: 1 addition & 1 deletion gradient-labs-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>ai.gradientlabs</groupId>
<artifactId>gradient-labs-parent</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>1.1.0-SNAPSHOT</version>
</parent>

<artifactId>gradient-labs-spring-boot-starter</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>ai.gradientlabs</groupId>
<artifactId>gradient-labs-parent</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>1.1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Gradient Labs Java SDK</name>
Expand Down
Loading