From d15b27e1fae6bf98cb5c6cb3c49d96b11c3c244e Mon Sep 17 00:00:00 2001 From: Irina Bednova Date: Wed, 22 Jul 2026 13:23:16 +0100 Subject: [PATCH 1/2] [conversations] add bulk memories upload endpoint Add Conversations.uploadMemories for POST /conversations/{id}/memories, which bulk uploads a batch of memories scoped to a conversation for the agent to search over on demand. Includes params/result models, an HTTP test, and an example. Co-Authored-By: Claude Fable 5 --- examples/conversations/index.ts | 12 +++++++++++- src/models/conversations.ts | 20 ++++++++++++++++++++ src/resources/conversations.ts | 14 ++++++++++++++ test/http.test.ts | 26 ++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/examples/conversations/index.ts b/examples/conversations/index.ts index 5856f1f..bf7ba61 100644 --- a/examples/conversations/index.ts +++ b/examples/conversations/index.ts @@ -1,6 +1,6 @@ /** * Conversations example: starts a conversation, sends a customer message, reads - * it back, then finishes it. + * it back, uploads a batch of memories, then finishes it. * * In your own project, import from the published package: * import { GradientLabs } from "@gradientlabs/client"; @@ -37,6 +37,16 @@ async function main(): Promise { const fetched = await client.conversations.get(id); console.log("Read conversation, latest intent:", fetched.latest_intent || "(none yet)"); + const upload = await client.conversations.uploadMemories(id, { + idempotency_key: `memories-${Date.now()}`, + memories: [ + { kind: "order", order_id: "A-1001", status: "shipped", occurred_at: "2026-01-01T00:00:00Z" }, + { kind: "preference", channel: "email" }, + ], + created_at_keys: ["occurred_at"], + }); + console.log("Uploaded memories:", upload.upload_id, upload.memories_inserted); + await client.conversations.finish(id, { reason: "example complete", reason_code: "customer-ended-chat", diff --git a/src/models/conversations.ts b/src/models/conversations.ts index b56ffb8..91a7713 100644 --- a/src/models/conversations.ts +++ b/src/models/conversations.ts @@ -133,6 +133,26 @@ export interface ReadConversationParams { support_platform?: string; } +export interface MemoriesBulkUploadParams { + /** + * De-duplicates retries of the same upload. Re-uploading with the same key + * returns the original upload instead of inserting again. + */ + idempotency_key: string; + /** The individual memories to store; each element is kept verbatim as the memory's raw payload. */ + memories: Record[]; + /** + * JSON keys tried in order to read each memory's timestamp from its payload. + * When none match, the upload time is used. + */ + created_at_keys?: string[]; +} + +export interface MemoriesBulkUploadResult { + upload_id: string; + memories_inserted: number; +} + export interface StartOutboundConversationParams { customer_id: string; customer_source: CustomerSource; diff --git a/src/resources/conversations.ts b/src/resources/conversations.ts index e90be1b..357aaba 100644 --- a/src/resources/conversations.ts +++ b/src/resources/conversations.ts @@ -7,6 +7,8 @@ import type { Conversation, ConversationEventParams, FinishConversationParams, + MemoriesBulkUploadParams, + MemoriesBulkUploadResult, Message, ReadConversationParams, RateConversationParams, @@ -121,6 +123,18 @@ export class Conversations { }); } + /** Bulk uploads a batch of memories scoped to a conversation for the agent to search over on demand. */ + uploadMemories( + id: string, + params: MemoriesBulkUploadParams, + config: RequestConfig = {}, + ): Promise { + return this.http.request("POST", `conversations/${encodeURIComponent(id)}/memories`, { + body: params, + signal: config.signal, + }); + } + /** Returns the result of an async tool execution. */ returnAsyncToolResult( id: string, diff --git a/test/http.test.ts b/test/http.test.ts index f17eee2..94a90ba 100644 --- a/test/http.test.ts +++ b/test/http.test.ts @@ -154,6 +154,32 @@ describe("HttpClient", () => { expect(record[0]!.body).toBeUndefined(); }); + it("bulk uploads conversation memories and returns the upload result", async () => { + const record: RecordedRequest[] = []; + const client = new GradientLabs({ + apiKey: "sk_test_123", + fetch: fakeFetch( + { status: 200, body: JSON.stringify({ upload_id: "upl_1", memories_inserted: 2 }) }, + record, + ), + }); + + const result = await client.conversations.uploadMemories("conv_1", { + idempotency_key: "key-123", + memories: [{ note: "prefers email" }, { order_id: "A1", ts: "2026-01-01T00:00:00Z" }], + created_at_keys: ["ts", "created_at"], + }); + + expect(result).toEqual({ upload_id: "upl_1", memories_inserted: 2 }); + expect(record).toHaveLength(1); + expect(record[0]!.method).toBe("POST"); + expect(record[0]!.input).toBe("https://api.gradient-labs.ai/conversations/conv_1/memories"); + const body = JSON.parse(record[0]!.body!); + expect(body.idempotency_key).toBe("key-123"); + expect(body.memories).toHaveLength(2); + expect(body.created_at_keys).toEqual(["ts", "created_at"]); + }); + it("respects a custom base URL", async () => { const record: RecordedRequest[] = []; const client = new GradientLabs({ From ee2ecc7b9fd908c4f84b01464e406d7a52ac6338 Mon Sep 17 00:00:00 2001 From: Irina Bednova Date: Wed, 22 Jul 2026 13:23:35 +0100 Subject: [PATCH 2/2] [release] v0.2.0 Minor bump for the new bulk memories upload endpoint. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- src/internal/version.ts | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa87326..6c63cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.0] + +### Added + +- `Conversations.uploadMemories` for `POST /conversations/{id}/memories`, which + bulk uploads a batch of memories scoped to a conversation for the agent to + search over on demand. + +## [0.1.2] + ### Added - Initial release of the Gradient Labs Node.js / TypeScript client. diff --git a/package.json b/package.json index d1498e1..33bf9d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gradientlabs/client", - "version": "0.1.2", + "version": "0.2.0", "description": "Official Node.js / TypeScript client for the Gradient Labs API", "type": "module", "engines": { diff --git a/src/internal/version.ts b/src/internal/version.ts index b8fa277..80e036e 100644 --- a/src/internal/version.ts +++ b/src/internal/version.ts @@ -1,2 +1,2 @@ // Kept in sync with the "version" field in package.json. -export const VERSION = "0.1.2"; +export const VERSION = "0.2.0";