From 9cc49f93ef99d744893db8fc117ae7ebfb2ad821 Mon Sep 17 00:00:00 2001 From: nikhilbajaj31 Date: Wed, 25 Mar 2026 17:49:09 +0530 Subject: [PATCH 1/6] fix(sdk-golang): require documentId for insert operations Make documentId a required field for IngestMemory, InsertDocument, and InsertDocumentsBatch. Add runtime validation that rejects empty documentId. Update tests, example, and integration test. Co-Authored-By: Claude Opus 4.6 --- .../sdk-golang/example/integration_test.go | 13 +++---- packages/sdk-golang/example/main.go | 35 ++++++++++--------- packages/sdk-golang/tinyhumans/client.go | 4 +++ packages/sdk-golang/tinyhumans/client_test.go | 34 ++++++++++++------ packages/sdk-golang/tinyhumans/endpoints.go | 18 ++++++---- .../sdk-golang/tinyhumans/endpoints_test.go | 29 +++++++++++---- packages/sdk-golang/tinyhumans/tinyhumans.go | 16 ++++----- 7 files changed, 94 insertions(+), 55 deletions(-) diff --git a/packages/sdk-golang/example/integration_test.go b/packages/sdk-golang/example/integration_test.go index 2a3883d..ad85005 100644 --- a/packages/sdk-golang/example/integration_test.go +++ b/packages/sdk-golang/example/integration_test.go @@ -28,12 +28,13 @@ func TestIntegration_InsertRecallDelete(t *testing.T) { // --- Insert --- now := float64(time.Now().Unix()) insertResp, err := client.IngestMemory(tinyhumans.MemoryItem{ - Key: "test-key-1", - Content: "The capital of France is Paris.", - Namespace: namespace, - Metadata: map[string]interface{}{"source": "integration-test"}, - CreatedAt: &now, - UpdatedAt: &now, + Key: "test-key-1", + Content: "The capital of France is Paris.", + Namespace: namespace, + DocumentID: "integration-test-doc-1", + Metadata: map[string]interface{}{"source": "integration-test"}, + CreatedAt: &now, + UpdatedAt: &now, }) if err != nil { t.Fatalf("IngestMemory: %v", err) diff --git a/packages/sdk-golang/example/main.go b/packages/sdk-golang/example/main.go index be52e16..32d7caa 100644 --- a/packages/sdk-golang/example/main.go +++ b/packages/sdk-golang/example/main.go @@ -24,12 +24,13 @@ func main() { // Ingest (upsert) a single memory now := float64(time.Now().Unix()) result, err := client.IngestMemory(tinyhumans.MemoryItem{ - Key: "user-preference-theme", - Content: "User prefers dark mode", - Namespace: "preferences", - Metadata: map[string]interface{}{"source": "onboarding"}, - CreatedAt: &now, - UpdatedAt: &now, + Key: "user-preference-theme", + Content: "User prefers dark mode", + Namespace: "preferences", + DocumentID: "pref-theme-001", + Metadata: map[string]interface{}{"source": "onboarding"}, + CreatedAt: &now, + UpdatedAt: &now, }) if err != nil { log.Fatal(err) @@ -39,15 +40,17 @@ func main() { // Ingest multiple memories batch, err := client.IngestMemories([]tinyhumans.MemoryItem{ { - Key: "go-sdk-example-1", - Content: "Go SDK can ingest multiple memories.", - Namespace: "preferences", - Metadata: map[string]interface{}{"source": "go-example"}, + Key: "go-sdk-example-1", + Content: "Go SDK can ingest multiple memories.", + Namespace: "preferences", + DocumentID: "go-example-001", + Metadata: map[string]interface{}{"source": "go-example"}, }, { - Key: "go-sdk-example-2", - Content: "This is a second memory from the Go example.", - Namespace: "preferences", + Key: "go-sdk-example-2", + Content: "This is a second memory from the Go example.", + Namespace: "preferences", + DocumentID: "go-example-002", }, }) if err != nil { @@ -93,7 +96,7 @@ func main() { // --- Document operations --- // Insert a single document - docResult, err := client.InsertDocument("Meeting Notes", "Discussed Q2 roadmap priorities.", "docs", &tinyhumans.InsertDocumentOptions{ + docResult, err := client.InsertDocument("Meeting Notes", "Discussed Q2 roadmap priorities.", "docs", "meeting-001", &tinyhumans.InsertDocumentOptions{ SourceType: "note", Metadata: map[string]interface{}{"team": "engineering"}, }) @@ -105,8 +108,8 @@ func main() { // Batch insert documents batchResult, err := client.InsertDocumentsBatch([]tinyhumans.DocumentItem{ - {Title: "Doc A", Content: "Content A", Namespace: "docs"}, - {Title: "Doc B", Content: "Content B", Namespace: "docs"}, + {Title: "Doc A", Content: "Content A", Namespace: "docs", DocumentID: "doc-a-001"}, + {Title: "Doc B", Content: "Content B", Namespace: "docs", DocumentID: "doc-b-001"}, }) if err != nil { log.Printf("InsertDocumentsBatch: %v", err) diff --git a/packages/sdk-golang/tinyhumans/client.go b/packages/sdk-golang/tinyhumans/client.go index 40cf70d..05623bc 100644 --- a/packages/sdk-golang/tinyhumans/client.go +++ b/packages/sdk-golang/tinyhumans/client.go @@ -79,6 +79,9 @@ func (c *Client) IngestMemories(items []MemoryItem) (*IngestMemoryResponse, erro errCount := 0 for _, item := range items { + if item.DocumentID == "" { + return nil, errors.New("documentId is required for each memory item") + } if err := validateTimestamps(item.CreatedAt, item.UpdatedAt); err != nil { return nil, err } @@ -87,6 +90,7 @@ func (c *Client) IngestMemories(items []MemoryItem) (*IngestMemoryResponse, erro "title": item.Key, "content": item.Content, "namespace": item.Namespace, + "documentId": item.DocumentID, "sourceType": "doc", "metadata": item.Metadata, } diff --git a/packages/sdk-golang/tinyhumans/client_test.go b/packages/sdk-golang/tinyhumans/client_test.go index 3570893..73d4dfa 100644 --- a/packages/sdk-golang/tinyhumans/client_test.go +++ b/packages/sdk-golang/tinyhumans/client_test.go @@ -234,7 +234,7 @@ func TestIngestMemory_Completed(t *testing.T) { defer server.Close() c := testClient(t, server) - resp, err := c.IngestMemory(MemoryItem{Key: "key1", Content: "hello", Namespace: "ns"}) + resp, err := c.IngestMemory(MemoryItem{Key: "key1", Content: "hello", Namespace: "ns", DocumentID: "doc-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -250,7 +250,7 @@ func TestIngestMemory_Updated(t *testing.T) { defer server.Close() c := testClient(t, server) - resp, err := c.IngestMemory(MemoryItem{Key: "key1", Content: "hello", Namespace: "ns"}) + resp, err := c.IngestMemory(MemoryItem{Key: "key1", Content: "hello", Namespace: "ns", DocumentID: "doc-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -267,7 +267,7 @@ func TestIngestMemory_ServerError(t *testing.T) { defer server.Close() c := testClient(t, server) - resp, err := c.IngestMemory(MemoryItem{Key: "key1", Content: "hello", Namespace: "ns"}) + resp, err := c.IngestMemory(MemoryItem{Key: "key1", Content: "hello", Namespace: "ns", DocumentID: "doc-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -284,10 +284,21 @@ func TestIngestMemories_EmptyList(t *testing.T) { } } +func TestIngestMemory_MissingDocumentID(t *testing.T) { + c, _ := NewClient("tok") + _, err := c.IngestMemory(MemoryItem{Key: "k", Content: "c", Namespace: "ns"}) + if err == nil { + t.Fatal("expected error for missing documentId") + } + if !strings.Contains(err.Error(), "documentId") { + t.Errorf("error should mention documentId, got: %v", err) + } +} + func TestIngestMemory_InvalidTimestamp(t *testing.T) { c, _ := NewClient("tok") neg := -1.0 - _, err := c.IngestMemory(MemoryItem{Key: "k", Content: "c", Namespace: "ns", CreatedAt: &neg}) + _, err := c.IngestMemory(MemoryItem{Key: "k", Content: "c", Namespace: "ns", DocumentID: "doc-1", CreatedAt: &neg}) if err == nil { t.Fatal("expected validation error for negative timestamp") } @@ -329,12 +340,13 @@ func TestIngestMemory_RequestBody(t *testing.T) { c := testClient(t, server) c.IngestMemory(MemoryItem{ - Key: "mykey", - Content: "mycontent", - Namespace: "myns", - Metadata: map[string]interface{}{"src": "test"}, - CreatedAt: &ts, - UpdatedAt: &ts, + Key: "mykey", + Content: "mycontent", + Namespace: "myns", + DocumentID: "doc-1", + Metadata: map[string]interface{}{"src": "test"}, + CreatedAt: &ts, + UpdatedAt: &ts, }) } @@ -355,7 +367,7 @@ func TestIngestMemory_NilMetadataBecomesEmptyMap(t *testing.T) { defer server.Close() c := testClient(t, server) - c.IngestMemory(MemoryItem{Key: "k", Content: "c", Namespace: "ns"}) + c.IngestMemory(MemoryItem{Key: "k", Content: "c", Namespace: "ns", DocumentID: "doc-1"}) } // --- RecallMemory --- diff --git a/packages/sdk-golang/tinyhumans/endpoints.go b/packages/sdk-golang/tinyhumans/endpoints.go index b18dc5b..30ae525 100644 --- a/packages/sdk-golang/tinyhumans/endpoints.go +++ b/packages/sdk-golang/tinyhumans/endpoints.go @@ -216,7 +216,7 @@ func (c *Client) sendInteraction(path, namespace string, entityNames []string, o // InsertDocument ingests a single document. // POST /memory/documents -func (c *Client) InsertDocument(title, content, namespace string, opts *InsertDocumentOptions) (map[string]interface{}, error) { +func (c *Client) InsertDocument(title, content, namespace, documentID string, opts *InsertDocumentOptions) (map[string]interface{}, error) { if title == "" { return nil, errors.New("title is required") } @@ -226,11 +226,15 @@ func (c *Client) InsertDocument(title, content, namespace string, opts *InsertDo if namespace == "" { return nil, errors.New("namespace is required") } + if documentID == "" { + return nil, errors.New("documentId is required") + } body := map[string]interface{}{ - "title": title, - "content": content, - "namespace": namespace, + "title": title, + "content": content, + "namespace": namespace, + "documentId": documentID, } if opts != nil { if opts.SourceType != "" { @@ -248,9 +252,6 @@ func (c *Client) InsertDocument(title, content, namespace string, opts *InsertDo if opts.UpdatedAt != nil { body["updatedAt"] = *opts.UpdatedAt } - if opts.DocumentID != "" { - body["documentId"] = opts.DocumentID - } } return c.send("POST", "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/memory/documents", body) @@ -272,6 +273,9 @@ func (c *Client) InsertDocumentsBatch(items []DocumentItem) (map[string]interfac if item.Namespace == "" { return nil, fmt.Errorf("item[%d]: namespace is required", i) } + if item.DocumentID == "" { + return nil, fmt.Errorf("item[%d]: documentId is required", i) + } } body := map[string]interface{}{ diff --git a/packages/sdk-golang/tinyhumans/endpoints_test.go b/packages/sdk-golang/tinyhumans/endpoints_test.go index 976644b..3965c8f 100644 --- a/packages/sdk-golang/tinyhumans/endpoints_test.go +++ b/packages/sdk-golang/tinyhumans/endpoints_test.go @@ -397,7 +397,7 @@ func TestInsertDocument_Success(t *testing.T) { defer server.Close() c := testClient(t, server) - data, err := c.InsertDocument("doc1", "content1", "ns1", nil) + data, err := c.InsertDocument("doc1", "content1", "ns1", "doc-123", nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -408,12 +408,20 @@ func TestInsertDocument_Success(t *testing.T) { func TestInsertDocument_EmptyTitle(t *testing.T) { c, _ := NewClient("tok") - _, err := c.InsertDocument("", "content", "ns", nil) + _, err := c.InsertDocument("", "content", "ns", "doc-1", nil) if err == nil { t.Fatal("expected error for empty title") } } +func TestInsertDocument_EmptyDocumentID(t *testing.T) { + c, _ := NewClient("tok") + _, err := c.InsertDocument("title", "content", "ns", "", nil) + if err == nil { + t.Fatal("expected error for empty documentId") + } +} + func TestInsertDocument_WithOptions(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body map[string]interface{} @@ -429,9 +437,8 @@ func TestInsertDocument_WithOptions(t *testing.T) { defer server.Close() c := testClient(t, server) - c.InsertDocument("t", "c", "ns", &InsertDocumentOptions{ + c.InsertDocument("t", "c", "ns", "custom-id", &InsertDocumentOptions{ SourceType: "pdf", - DocumentID: "custom-id", }) } @@ -448,8 +455,8 @@ func TestInsertDocumentsBatch_Success(t *testing.T) { c := testClient(t, server) data, err := c.InsertDocumentsBatch([]DocumentItem{ - {Title: "d1", Content: "c1", Namespace: "ns"}, - {Title: "d2", Content: "c2", Namespace: "ns"}, + {Title: "d1", Content: "c1", Namespace: "ns", DocumentID: "doc-1"}, + {Title: "d2", Content: "c2", Namespace: "ns", DocumentID: "doc-2"}, }) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -469,12 +476,20 @@ func TestInsertDocumentsBatch_EmptyItems(t *testing.T) { func TestInsertDocumentsBatch_MissingTitle(t *testing.T) { c, _ := NewClient("tok") - _, err := c.InsertDocumentsBatch([]DocumentItem{{Content: "c", Namespace: "ns"}}) + _, err := c.InsertDocumentsBatch([]DocumentItem{{Content: "c", Namespace: "ns", DocumentID: "doc-1"}}) if err == nil { t.Fatal("expected error for missing title") } } +func TestInsertDocumentsBatch_MissingDocumentID(t *testing.T) { + c, _ := NewClient("tok") + _, err := c.InsertDocumentsBatch([]DocumentItem{{Title: "t", Content: "c", Namespace: "ns"}}) + if err == nil { + t.Fatal("expected error for missing documentId") + } +} + // --- ListDocuments --- func TestListDocuments_Success(t *testing.T) { diff --git a/packages/sdk-golang/tinyhumans/tinyhumans.go b/packages/sdk-golang/tinyhumans/tinyhumans.go index f36c3f1..2e2ca9f 100644 --- a/packages/sdk-golang/tinyhumans/tinyhumans.go +++ b/packages/sdk-golang/tinyhumans/tinyhumans.go @@ -14,12 +14,13 @@ const ( // MemoryItem represents a single memory item to ingest. type MemoryItem struct { - Key string `json:"key"` - Content string `json:"content"` - Namespace string `json:"namespace"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - CreatedAt *float64 `json:"created_at,omitempty"` - UpdatedAt *float64 `json:"updated_at,omitempty"` + Key string `json:"key"` + Content string `json:"content"` + Namespace string `json:"namespace"` + DocumentID string `json:"documentId"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + CreatedAt *float64 `json:"created_at,omitempty"` + UpdatedAt *float64 `json:"updated_at,omitempty"` } // ReadMemoryItem represents a memory item returned from recall. @@ -151,7 +152,6 @@ type InsertDocumentOptions struct { Priority string CreatedAt *float64 UpdatedAt *float64 - DocumentID string } // DocumentItem represents a single document in a batch insert. @@ -164,7 +164,7 @@ type DocumentItem struct { Priority string `json:"priority,omitempty"` CreatedAt *float64 `json:"createdAt,omitempty"` UpdatedAt *float64 `json:"updatedAt,omitempty"` - DocumentID string `json:"documentId,omitempty"` + DocumentID string `json:"documentId"` } // ListDocumentsOptions holds optional parameters for ListDocuments. From 132d09ca47ed51f9738c3152f47fdae0164d74da Mon Sep 17 00:00:00 2001 From: nikhilbajaj31 Date: Wed, 25 Mar 2026 17:54:37 +0530 Subject: [PATCH 2/6] fix(sdk-java): require documentId for insert operations Make documentId a required field in InsertMemoryParams and InsertDocumentParams with validation in validate(). Always include documentId in toMap() output. Update tests, example, and integration test. Co-Authored-By: Claude Opus 4.6 --- packages/sdk-java/example/ExampleUsage.java | 7 +++-- .../tinyhumans/sdk/InsertDocumentParams.java | 5 +++- .../tinyhumans/sdk/InsertMemoryParams.java | 5 +++- .../xyz/tinyhumans/sdk/IntegrationTest.java | 7 +++-- .../sdk/TinyHumansMemoryClientTest.java | 30 ++++++++++++++----- 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/packages/sdk-java/example/ExampleUsage.java b/packages/sdk-java/example/ExampleUsage.java index d9d4181..2d8aca1 100644 --- a/packages/sdk-java/example/ExampleUsage.java +++ b/packages/sdk-java/example/ExampleUsage.java @@ -28,6 +28,7 @@ public static void main(String[] args) throws InterruptedException { System.out.println("=== Insert Memory ==="); InsertMemoryResponse insertResp = client.insertMemory( new InsertMemoryParams("greeting", "Hello from Java SDK!", namespace) + .setDocumentId("java-greeting-001") .setMetadata(Map.of("lang", "java"))); System.out.println("Success: " + insertResp.isSuccess() + ", Status: " + insertResp.getStatus()); @@ -96,7 +97,7 @@ public static void main(String[] args) throws InterruptedException { System.out.println("\n=== Insert Document ==="); try { Map docResp = client.insertDocument( - new InsertDocumentParams("Java Guide", "Java SDK usage guide", namespace)); + new InsertDocumentParams("Java Guide", "Java SDK usage guide", namespace).setDocumentId("java-guide-001")); System.out.println("InsertDocument: " + docResp); } catch (Exception e) { System.out.println("InsertDocument: " + e.getMessage()); @@ -107,8 +108,8 @@ public static void main(String[] args) throws InterruptedException { try { Map batchResp = client.insertDocumentsBatch( new InsertDocumentsBatchParams(List.of( - new InsertDocumentParams("Doc 1", "Content 1", namespace), - new InsertDocumentParams("Doc 2", "Content 2", namespace)))); + new InsertDocumentParams("Doc 1", "Content 1", namespace).setDocumentId("doc-001"), + new InsertDocumentParams("Doc 2", "Content 2", namespace).setDocumentId("doc-002")))); System.out.println("InsertDocumentsBatch: " + batchResp); } catch (Exception e) { System.out.println("InsertDocumentsBatch: " + e.getMessage()); diff --git a/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertDocumentParams.java b/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertDocumentParams.java index 686c2d8..0e699a4 100644 --- a/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertDocumentParams.java +++ b/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertDocumentParams.java @@ -33,6 +33,9 @@ public void validate() { if (namespace == null || namespace.isEmpty()) { throw new IllegalArgumentException("namespace is required"); } + if (documentId == null || documentId.isEmpty()) { + throw new IllegalArgumentException("documentId is required"); + } } public Map toMap() { @@ -46,7 +49,7 @@ public Map toMap() { if (priority != null) map.put("priority", priority); if (createdAt != null) map.put("createdAt", createdAt); if (updatedAt != null) map.put("updatedAt", updatedAt); - if (documentId != null) map.put("documentId", documentId); + map.put("documentId", documentId); return map; } diff --git a/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertMemoryParams.java b/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertMemoryParams.java index f545bea..71a676e 100644 --- a/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertMemoryParams.java +++ b/packages/sdk-java/src/main/java/xyz/tinyhumans/sdk/InsertMemoryParams.java @@ -33,6 +33,9 @@ public void validate() { if (namespace == null || namespace.isEmpty()) { throw new IllegalArgumentException("namespace is required and must be a non-empty string"); } + if (documentId == null || documentId.isEmpty()) { + throw new IllegalArgumentException("documentId is required and must be a non-empty string"); + } } public Map toMap() { @@ -46,7 +49,7 @@ public Map toMap() { if (priority != null) map.put("priority", priority); if (createdAt != null) map.put("createdAt", createdAt); if (updatedAt != null) map.put("updatedAt", updatedAt); - if (documentId != null) map.put("documentId", documentId); + map.put("documentId", documentId); return map; } diff --git a/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/IntegrationTest.java b/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/IntegrationTest.java index 9a39bb0..2daa9c9 100644 --- a/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/IntegrationTest.java +++ b/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/IntegrationTest.java @@ -22,6 +22,7 @@ void insertRecallDeleteLifecycle() throws InterruptedException { long now = System.currentTimeMillis() / 1000; InsertMemoryResponse insertResp = client.insertMemory( new InsertMemoryParams("test-key-1", "The capital of France is Paris.", namespace) + .setDocumentId("integration-test-doc-1") .setMetadata(Map.of("source", "integration-test")) .setCreatedAt(now) .setUpdatedAt(now)); @@ -41,14 +42,14 @@ void insertRecallDeleteLifecycle() throws InterruptedException { // --- Insert Document --- Map docResp = client.insertDocument( - new InsertDocumentParams("Test Doc", "Document content", namespace)); + new InsertDocumentParams("Test Doc", "Document content", namespace).setDocumentId("test-doc-1")); System.out.println("InsertDocument: " + docResp); // --- Insert Documents Batch --- Map batchResp = client.insertDocumentsBatch( new InsertDocumentsBatchParams(List.of( - new InsertDocumentParams("Batch 1", "Content 1", namespace), - new InsertDocumentParams("Batch 2", "Content 2", namespace)))); + new InsertDocumentParams("Batch 1", "Content 1", namespace).setDocumentId("batch-doc-1"), + new InsertDocumentParams("Batch 2", "Content 2", namespace).setDocumentId("batch-doc-2")))); System.out.println("InsertDocumentsBatch: " + batchResp); // --- List Documents --- diff --git a/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/TinyHumansMemoryClientTest.java b/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/TinyHumansMemoryClientTest.java index c57009c..10393a7 100644 --- a/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/TinyHumansMemoryClientTest.java +++ b/packages/sdk-java/src/test/java/xyz/tinyhumans/sdk/TinyHumansMemoryClientTest.java @@ -66,7 +66,7 @@ void defaultModelIdIsNeocortexMk1() { }); try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("tok", baseUrl)) { - client.insertMemory(new InsertMemoryParams("t", "c", "n")); + client.insertMemory(new InsertMemoryParams("t", "c", "n").setDocumentId("doc-1")); } } @@ -80,7 +80,7 @@ void customModelIdSentInHeader() { }); try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("tok", "custom-model", baseUrl)) { - client.insertMemory(new InsertMemoryParams("t", "c", "n")); + client.insertMemory(new InsertMemoryParams("t", "c", "n").setDocumentId("doc-1")); } } @@ -94,7 +94,7 @@ void emptyModelIdDefaultsToNeocortex() { }); try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("tok", "", baseUrl)) { - client.insertMemory(new InsertMemoryParams("t", "c", "n")); + client.insertMemory(new InsertMemoryParams("t", "c", "n").setDocumentId("doc-1")); } } @@ -180,7 +180,7 @@ void insertMemorySendsCorrectRequest() { try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("test-token", baseUrl)) { InsertMemoryResponse resp = client.insertMemory( - new InsertMemoryParams("title", "content", "ns")); + new InsertMemoryParams("title", "content", "ns").setDocumentId("doc-1")); assertTrue(resp.isSuccess()); assertEquals("completed", resp.getStatus()); } @@ -210,6 +210,14 @@ void insertMemoryValidatesMissingNamespace() { } } + @Test + void insertMemoryValidatesMissingDocumentId() { + try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("test-token", baseUrl)) { + assertThrows(IllegalArgumentException.class, () -> + client.insertMemory(new InsertMemoryParams("title", "content", "ns"))); + } + } + // ---- recallMemory ---- @Test @@ -478,7 +486,7 @@ void insertDocumentSuccess() { try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("tok", baseUrl)) { Map resp = client.insertDocument( - new InsertDocumentParams("title", "content", "ns")); + new InsertDocumentParams("title", "content", "ns").setDocumentId("doc-1")); assertNotNull(resp.get("data")); } } @@ -491,6 +499,14 @@ void insertDocumentRejectsMissingTitle() { } } + @Test + void insertDocumentRejectsMissingDocumentId() { + try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("tok", baseUrl)) { + assertThrows(IllegalArgumentException.class, () -> + client.insertDocument(new InsertDocumentParams("title", "content", "ns"))); + } + } + // ---- insertDocumentsBatch ---- @Test @@ -505,7 +521,7 @@ void insertDocumentsBatchSuccess() { try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("tok", baseUrl)) { Map resp = client.insertDocumentsBatch( new InsertDocumentsBatchParams(List.of( - new InsertDocumentParams("t1", "c1", "ns")))); + new InsertDocumentParams("t1", "c1", "ns").setDocumentId("doc-1")))); assertNotNull(resp.get("data")); } } @@ -822,7 +838,7 @@ void serverErrorThrowsWithHttpStatus() { try (TinyHumansMemoryClient client = new TinyHumansMemoryClient("test-token", baseUrl)) { TinyHumansError err = assertThrows(TinyHumansError.class, () -> - client.insertMemory(new InsertMemoryParams("t", "c", "n"))); + client.insertMemory(new InsertMemoryParams("t", "c", "n").setDocumentId("doc-1"))); assertEquals(500, err.getStatus()); assertTrue(err.getMessage().contains("500")); } From 5fd89a84e6511c21fcbe7dea1e517d386ca661b3 Mon Sep 17 00:00:00 2001 From: nikhilbajaj31 Date: Wed, 25 Mar 2026 18:00:02 +0530 Subject: [PATCH 3/6] fix(sdk-dart): require documentId for insert operations Make documentId required in InsertMemoryParams (via validate()) and InsertDocumentParams (via required named parameter). Add validation tests. Update example and integration test. Co-Authored-By: Claude Opus 4.6 --- packages/sdk-dart/example/example.dart | 6 ++-- packages/sdk-dart/lib/src/types.dart | 11 ++++++- packages/sdk-dart/test/integration_test.dart | 6 ++-- .../test/tinyhumans_memory_client_test.dart | 30 +++++++++++++++++-- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/packages/sdk-dart/example/example.dart b/packages/sdk-dart/example/example.dart index 5fd0857..2262928 100644 --- a/packages/sdk-dart/example/example.dart +++ b/packages/sdk-dart/example/example.dart @@ -19,6 +19,7 @@ Future main() async { title: 'example-doc', content: 'Dart was created by Google and first appeared in 2011.', namespace: ns, + documentId: 'dart-example-001', )); print('Success=${insertResp.success} Status=${insertResp.status}'); @@ -101,6 +102,7 @@ Future main() async { title: 'Dart Guide', content: 'Dart SDK usage guide', namespace: ns, + documentId: 'dart-guide-001', )); print('InsertDocument: $docResp'); } catch (e) { @@ -114,9 +116,9 @@ Future main() async { await client.insertDocumentsBatch(InsertDocumentsBatchParams( documents: [ InsertDocumentParams( - title: 'Doc 1', content: 'Content 1', namespace: ns), + title: 'Doc 1', content: 'Content 1', namespace: ns, documentId: 'doc-001'), InsertDocumentParams( - title: 'Doc 2', content: 'Content 2', namespace: ns), + title: 'Doc 2', content: 'Content 2', namespace: ns, documentId: 'doc-002'), ], )); print('InsertDocumentsBatch: $batchResp'); diff --git a/packages/sdk-dart/lib/src/types.dart b/packages/sdk-dart/lib/src/types.dart index 73eb7e7..0b6609e 100644 --- a/packages/sdk-dart/lib/src/types.dart +++ b/packages/sdk-dart/lib/src/types.dart @@ -33,6 +33,9 @@ class InsertMemoryParams { if (namespace == null || namespace!.trim().isEmpty) { throw ArgumentError('namespace is required and must be a string'); } + if (documentId == null || documentId!.trim().isEmpty) { + throw ArgumentError('documentId is required and must be a non-empty string'); + } } Map toJson() { @@ -41,12 +44,12 @@ class InsertMemoryParams { 'content': content, 'namespace': namespace, 'sourceType': sourceType, + 'documentId': documentId, }; if (metadata != null) map['metadata'] = metadata; if (priority != null) map['priority'] = priority; if (createdAt != null) map['createdAt'] = createdAt; if (updatedAt != null) map['updatedAt'] = updatedAt; - if (documentId != null) map['documentId'] = documentId; return map; } } @@ -460,6 +463,7 @@ class InsertDocumentParams { final String title; final String content; final String namespace; + final String documentId; final Map? metadata; final String? sourceType; @@ -467,6 +471,7 @@ class InsertDocumentParams { required this.title, required this.content, required this.namespace, + required this.documentId, this.metadata, this.sourceType, }); @@ -481,6 +486,9 @@ class InsertDocumentParams { if (namespace.trim().isEmpty) { throw ArgumentError('namespace is required'); } + if (documentId.trim().isEmpty) { + throw ArgumentError('documentId is required'); + } } Map toJson() { @@ -488,6 +496,7 @@ class InsertDocumentParams { 'title': title, 'content': content, 'namespace': namespace, + 'documentId': documentId, }; if (metadata != null) map['metadata'] = metadata; if (sourceType != null) map['sourceType'] = sourceType; diff --git a/packages/sdk-dart/test/integration_test.dart b/packages/sdk-dart/test/integration_test.dart index c5b0c33..3571ef2 100644 --- a/packages/sdk-dart/test/integration_test.dart +++ b/packages/sdk-dart/test/integration_test.dart @@ -23,6 +23,7 @@ void main() { title: 'test-key-1', content: 'The capital of France is Paris.', namespace: ns, + documentId: 'integration-test-doc-1', metadata: {'source': 'integration-test'}, createdAt: nowSeconds, updatedAt: nowSeconds, @@ -51,6 +52,7 @@ void main() { title: 'Test Doc', content: 'Document content for integration test', namespace: ns, + documentId: 'test-doc-1', )); print('InsertDocument: $docResp'); } catch (e) { @@ -63,9 +65,9 @@ void main() { await client.insertDocumentsBatch(InsertDocumentsBatchParams( documents: [ InsertDocumentParams( - title: 'Batch 1', content: 'Content 1', namespace: ns), + title: 'Batch 1', content: 'Content 1', namespace: ns, documentId: 'batch-doc-1'), InsertDocumentParams( - title: 'Batch 2', content: 'Content 2', namespace: ns), + title: 'Batch 2', content: 'Content 2', namespace: ns, documentId: 'batch-doc-2'), ], )); print('InsertDocumentsBatch: $batchResp'); diff --git a/packages/sdk-dart/test/tinyhumans_memory_client_test.dart b/packages/sdk-dart/test/tinyhumans_memory_client_test.dart index 76fdfc1..73155e1 100644 --- a/packages/sdk-dart/test/tinyhumans_memory_client_test.dart +++ b/packages/sdk-dart/test/tinyhumans_memory_client_test.dart @@ -106,6 +106,7 @@ void main() { title: 't1', content: 'c1', namespace: 'ns1', + documentId: 'doc-1', )); expect(resp.success, isTrue); @@ -139,6 +140,7 @@ void main() { title: 't', content: 'c', namespace: 'ns', + documentId: 'doc-1', )); expect(resp.usage, isNotNull); @@ -170,6 +172,15 @@ void main() { throwsA(isA()), ); }); + + test('throws on missing documentId', () { + final client = createClient(); + expect( + () => client.insertMemory( + InsertMemoryParams(title: 't', content: 'c', namespace: 'ns')), + throwsA(isA()), + ); + }); }); // ── RecallMemory ── @@ -493,6 +504,7 @@ void main() { title: 'Doc Title', content: 'Doc content', namespace: 'ns', + documentId: 'doc-1', )); expect(captured!.method, equals('POST')); @@ -510,6 +522,20 @@ void main() { title: '', content: 'c', namespace: 'ns', + documentId: 'doc-1', + )), + throwsA(isA()), + ); + }); + + test('throws on empty documentId', () { + final client = createClient(); + expect( + () => client.insertDocument(InsertDocumentParams( + title: 't', + content: 'c', + namespace: 'ns', + documentId: '', )), throwsA(isA()), ); @@ -526,9 +552,9 @@ void main() { await client.insertDocumentsBatch(InsertDocumentsBatchParams( documents: [ InsertDocumentParams( - title: 'D1', content: 'C1', namespace: 'ns'), + title: 'D1', content: 'C1', namespace: 'ns', documentId: 'doc-1'), InsertDocumentParams( - title: 'D2', content: 'C2', namespace: 'ns'), + title: 'D2', content: 'C2', namespace: 'ns', documentId: 'doc-2'), ], )); From d3d9e51f7a59c03240da7775e519addbf2958851 Mon Sep 17 00:00:00 2001 From: nikhilbajaj31 Date: Wed, 25 Mar 2026 18:02:27 +0530 Subject: [PATCH 4/6] fix(sdk-cpp): require documentId for insertMemory Change document_id from std::optional to std::string in InsertMemoryParams. Add validation and always serialize. Update tests, example, and integration test. Co-Authored-By: Claude Opus 4.6 --- packages/sdk-cpp/example/example_usage.cpp | 1 + packages/sdk-cpp/include/tinyhumans/types.hpp | 5 +++-- packages/sdk-cpp/test/integration_test.cpp | 1 + packages/sdk-cpp/test/memory_client_test.cpp | 14 +++++++++++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/sdk-cpp/example/example_usage.cpp b/packages/sdk-cpp/example/example_usage.cpp index a159fed..a79ea63 100644 --- a/packages/sdk-cpp/example/example_usage.cpp +++ b/packages/sdk-cpp/example/example_usage.cpp @@ -25,6 +25,7 @@ int main() { insert_params.set_title("example-doc") .set_content("The speed of light is approximately 299,792 km/s.") .set_namespace(ns) + .set_document_id("cpp-example-001") .set_metadata(json{{"lang", "cpp"}}); auto insert_resp = client.insert_memory(insert_params); std::cout << "success=" << insert_resp.success << " status=" << insert_resp.status << std::endl; diff --git a/packages/sdk-cpp/include/tinyhumans/types.hpp b/packages/sdk-cpp/include/tinyhumans/types.hpp index 43f3590..daab3b0 100644 --- a/packages/sdk-cpp/include/tinyhumans/types.hpp +++ b/packages/sdk-cpp/include/tinyhumans/types.hpp @@ -22,7 +22,7 @@ struct InsertMemoryParams { std::optional priority; std::optional created_at; std::optional updated_at; - std::optional document_id; + std::string document_id; InsertMemoryParams& set_title(const std::string& v) { title = v; return *this; } InsertMemoryParams& set_content(const std::string& v) { content = v; return *this; } @@ -38,6 +38,7 @@ struct InsertMemoryParams { if (title.empty()) throw std::invalid_argument("title is required"); if (content.empty()) throw std::invalid_argument("content is required"); if (namespace_.empty()) throw std::invalid_argument("namespace is required"); + if (document_id.empty()) throw std::invalid_argument("documentId is required"); } json to_json() const { @@ -47,11 +48,11 @@ struct InsertMemoryParams { j["content"] = content; j["namespace"] = namespace_; j["sourceType"] = source_type; + j["documentId"] = document_id; if (metadata) j["metadata"] = *metadata; if (priority) j["priority"] = *priority; if (created_at) j["createdAt"] = *created_at; if (updated_at) j["updatedAt"] = *updated_at; - if (document_id) j["documentId"] = *document_id; return j; } }; diff --git a/packages/sdk-cpp/test/integration_test.cpp b/packages/sdk-cpp/test/integration_test.cpp index c19c333..bae8924 100644 --- a/packages/sdk-cpp/test/integration_test.cpp +++ b/packages/sdk-cpp/test/integration_test.cpp @@ -28,6 +28,7 @@ TEST(IntegrationTest, FullLifecycle) { insert_params.set_title("test-key-1") .set_content("The capital of France is Paris.") .set_namespace(ns) + .set_document_id("integration-test-doc-1") .set_metadata(json{{"source", "integration-test"}}) .set_created_at(now_s) .set_updated_at(now_s); diff --git a/packages/sdk-cpp/test/memory_client_test.cpp b/packages/sdk-cpp/test/memory_client_test.cpp index d19e28a..65677d9 100644 --- a/packages/sdk-cpp/test/memory_client_test.cpp +++ b/packages/sdk-cpp/test/memory_client_test.cpp @@ -156,7 +156,7 @@ TEST(MemoryClientTest, MockServerParsesMethodAndPath) { TinyHumansMemoryClient client("test-token", server.base_url()); InsertMemoryParams params; - params.set_title("t").set_content("c").set_namespace("n"); + params.set_title("t").set_content("c").set_namespace("n").set_document_id("doc-1"); client.insert_memory(params); future.get(); @@ -201,7 +201,7 @@ TEST(MemoryClientTest, InsertMemorySendsCorrectRequest) { TinyHumansMemoryClient client("test-token", server.base_url()); InsertMemoryParams params; - params.set_title("title").set_content("content").set_namespace("ns"); + params.set_title("title").set_content("content").set_namespace("ns").set_document_id("doc-1"); auto resp = client.insert_memory(params); std::string body = future.get(); @@ -244,6 +244,14 @@ TEST(MemoryClientTest, InsertMemoryValidatesMissingNamespace) { EXPECT_THROW(client.insert_memory(params), std::invalid_argument); } +TEST(MemoryClientTest, InsertMemoryValidatesMissingDocumentId) { + MockHttpServer server; + TinyHumansMemoryClient client("test-token", server.base_url()); + InsertMemoryParams params; + params.set_title("title").set_content("content").set_namespace("ns"); + EXPECT_THROW(client.insert_memory(params), std::invalid_argument); +} + // ---- recallMemory ---- TEST(MemoryClientTest, RecallMemoryParsesResponse) { @@ -689,7 +697,7 @@ TEST(MemoryClientTest, ServerErrorThrowsWithHttpStatus) { TinyHumansMemoryClient client("test-token", server.base_url()); try { InsertMemoryParams params; - params.set_title("t").set_content("c").set_namespace("n"); + params.set_title("t").set_content("c").set_namespace("n").set_document_id("doc-1"); client.insert_memory(params); FAIL() << "Expected TinyHumansError"; } catch (const TinyHumansError& err) { From 3ebae56a7ae84b4e257948952e2b3c12b3df51d9 Mon Sep 17 00:00:00 2001 From: nikhilbajaj31 Date: Wed, 25 Mar 2026 18:05:33 +0530 Subject: [PATCH 5/6] fix(sdk-csharp): require documentId for insertMemory Add validation for DocumentId in InsertMemoryParams.Validate() and always include it in ToJsonObject(). Update tests, example, and integration test. Co-Authored-By: Claude Opus 4.6 --- .../example/TinyHumans.Sdk.Example/Program.cs | 1 + packages/sdk-csharp/src/TinyHumans.Sdk/Types.cs | 4 +++- .../test/TinyHumans.Sdk.Tests/IntegrationTest.cs | 1 + .../TinyHumans.Sdk.Tests/MemoryClientTests.cs | 15 ++++++++++++--- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/sdk-csharp/example/TinyHumans.Sdk.Example/Program.cs b/packages/sdk-csharp/example/TinyHumans.Sdk.Example/Program.cs index 2607586..3fdfc1d 100644 --- a/packages/sdk-csharp/example/TinyHumans.Sdk.Example/Program.cs +++ b/packages/sdk-csharp/example/TinyHumans.Sdk.Example/Program.cs @@ -16,6 +16,7 @@ Title = "example-doc", Content = "The speed of light is approximately 299,792 km/s.", Namespace = ns, + DocumentId = "csharp-example-001", Metadata = new Dictionary { ["lang"] = "csharp" }, }); Console.WriteLine($"Success={insertResp.Success} Status={insertResp.Status}"); diff --git a/packages/sdk-csharp/src/TinyHumans.Sdk/Types.cs b/packages/sdk-csharp/src/TinyHumans.Sdk/Types.cs index d44fe47..85c57ab 100644 --- a/packages/sdk-csharp/src/TinyHumans.Sdk/Types.cs +++ b/packages/sdk-csharp/src/TinyHumans.Sdk/Types.cs @@ -24,6 +24,8 @@ public void Validate() throw new ArgumentException("content is required and must be a string"); if (string.IsNullOrWhiteSpace(Namespace)) throw new ArgumentException("namespace is required and must be a string"); + if (string.IsNullOrWhiteSpace(DocumentId)) + throw new ArgumentException("documentId is required and must be a non-empty string"); } public Dictionary ToJsonObject() @@ -34,12 +36,12 @@ public void Validate() ["content"] = Content, ["namespace"] = Namespace, ["sourceType"] = SourceType, + ["documentId"] = DocumentId, }; if (Metadata != null) dict["metadata"] = Metadata; if (Priority != null) dict["priority"] = Priority; if (CreatedAt.HasValue) dict["createdAt"] = CreatedAt.Value; if (UpdatedAt.HasValue) dict["updatedAt"] = UpdatedAt.Value; - if (DocumentId != null) dict["documentId"] = DocumentId; return dict; } } diff --git a/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/IntegrationTest.cs b/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/IntegrationTest.cs index 4f32275..bfb517c 100644 --- a/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/IntegrationTest.cs +++ b/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/IntegrationTest.cs @@ -34,6 +34,7 @@ public async Task FullLifecycle() Title = "test-key-1", Content = "The capital of France is Paris.", Namespace = ns, + DocumentId = "integration-test-doc-1", Metadata = new Dictionary { ["source"] = "integration-test" }, CreatedAt = nowSeconds, UpdatedAt = nowSeconds, diff --git a/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/MemoryClientTests.cs b/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/MemoryClientTests.cs index 0dfd590..2974b8a 100644 --- a/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/MemoryClientTests.cs +++ b/packages/sdk-csharp/test/TinyHumans.Sdk.Tests/MemoryClientTests.cs @@ -35,7 +35,7 @@ public async Task DefaultModelId_SendsNeocortexMk1Header() await client.InsertMemoryAsync(new InsertMemoryParams { - Title = "t", Content = "c", Namespace = "ns", + Title = "t", Content = "c", Namespace = "ns", DocumentId = "doc-1", }); Assert.NotNull(handler.CapturedRequest); @@ -54,7 +54,7 @@ public async Task CustomModelId_PropagatesCorrectly() await client.InsertMemoryAsync(new InsertMemoryParams { - Title = "t", Content = "c", Namespace = "ns", + Title = "t", Content = "c", Namespace = "ns", DocumentId = "doc-1", }); Assert.NotNull(handler.CapturedRequest); @@ -77,6 +77,7 @@ public async Task InsertMemory_SendsCorrectRequest() Title = "t1", Content = "c1", Namespace = "ns1", + DocumentId = "doc-1", }); Assert.True(resp.Success); @@ -104,7 +105,7 @@ public async Task InsertMemory_ParsesUsage() var resp = await client.InsertMemoryAsync(new InsertMemoryParams { - Title = "t", Content = "c", Namespace = "ns", + Title = "t", Content = "c", Namespace = "ns", DocumentId = "doc-1", }); Assert.NotNull(resp.Usage); @@ -134,6 +135,14 @@ await Assert.ThrowsAsync(() => client.InsertMemoryAsync(new InsertMemoryParams { Title = "t", Content = "c" })); } + [Fact] + public async Task InsertMemory_ThrowsOnMissingDocumentId() + { + using var client = CreateClient(); + await Assert.ThrowsAsync(() => + client.InsertMemoryAsync(new InsertMemoryParams { Title = "t", Content = "c", Namespace = "ns" })); + } + // ── RecallMemory ── [Fact] From ffbd6cfaf360a16e3e7a002911a7837ba8165b33 Mon Sep 17 00:00:00 2001 From: nikhilbajaj31 Date: Wed, 25 Mar 2026 19:58:21 +0530 Subject: [PATCH 6/6] fix(sdk-rust): require documentId for insert operations Change document_id from Option to String in InsertMemoryParams, IngestDocumentParams, and BatchDocumentItem. Add validation in insert_memory, ingest_document, and ingest_documents_batch. Update tests and examples. Co-Authored-By: Claude Opus 4.6 --- packages/sdk-rust/examples/test_routes.rs | 6 ++++-- packages/sdk-rust/src/lib.rs | 21 +++++++++++++++++++-- packages/sdk-rust/src/types.rs | 7 ++++--- packages/sdk-rust/tests/client_test.rs | 11 +++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/sdk-rust/examples/test_routes.rs b/packages/sdk-rust/examples/test_routes.rs index 859a07f..6632ef8 100644 --- a/packages/sdk-rust/examples/test_routes.rs +++ b/packages/sdk-rust/examples/test_routes.rs @@ -179,7 +179,7 @@ async fn main() { priority: None, created_at: None, updated_at: None, - document_id: Some(format!("{doc_single}-memory")), + document_id: format!("{doc_single}-memory"), }) .await; let insert_memory_data = push_result(&mut results, "insert_memory", insert_memory_res, false); @@ -237,7 +237,7 @@ async fn main() { priority: None, created_at: None, updated_at: None, - document_id: Some(doc_single.clone()), + document_id: doc_single.clone(), }) .await, false, @@ -265,11 +265,13 @@ async fn main() { title: "Rust Route Test Batch 1".to_string(), content: format!("Batch document 1 id={doc_batch_1}"), namespace: namespace.clone(), + document_id: doc_batch_1.clone(), }, BatchDocumentItem { title: "Rust Route Test Batch 2".to_string(), content: format!("Batch document 2 id={doc_batch_2}"), namespace: namespace.clone(), + document_id: doc_batch_2.clone(), }, ], }) diff --git a/packages/sdk-rust/src/lib.rs b/packages/sdk-rust/src/lib.rs index 4930764..60f6e72 100644 --- a/packages/sdk-rust/src/lib.rs +++ b/packages/sdk-rust/src/lib.rs @@ -102,6 +102,11 @@ impl TinyHumansMemoryClient { "namespace is required and must be a string".into(), )); } + if params.document_id.is_empty() { + return Err(TinyHumansError::Validation( + "documentId is required and must be a non-empty string".into(), + )); + } let body = InsertMemoryBody { title: params.title, content: params.content, @@ -274,6 +279,11 @@ impl TinyHumansMemoryClient { "namespace is required and must be a string".into(), )); } + if params.document_id.is_empty() { + return Err(TinyHumansError::Validation( + "documentId is required and must be a non-empty string".into(), + )); + } self.post("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/memory/documents", ¶ms).await } @@ -287,6 +297,13 @@ impl TinyHumansMemoryClient { "items must be a non-empty list".into(), )); } + for (i, item) in params.items.iter().enumerate() { + if item.document_id.is_empty() { + return Err(TinyHumansError::Validation( + format!("items[{}]: documentId is required", i), + )); + } + } self.post("/memory/documents/batch", ¶ms).await } @@ -460,8 +477,8 @@ struct InsertMemoryBody { created_at: Option, #[serde(rename = "updatedAt", skip_serializing_if = "Option::is_none")] updated_at: Option, - #[serde(rename = "documentId", skip_serializing_if = "Option::is_none")] - document_id: Option, + #[serde(rename = "documentId")] + document_id: String, } #[derive(serde::Deserialize, Default)] diff --git a/packages/sdk-rust/src/types.rs b/packages/sdk-rust/src/types.rs index ce83e82..45bed3f 100644 --- a/packages/sdk-rust/src/types.rs +++ b/packages/sdk-rust/src/types.rs @@ -61,7 +61,7 @@ pub struct InsertMemoryParams { pub priority: Option, pub created_at: Option, pub updated_at: Option, - pub document_id: Option, + pub document_id: String, } #[derive(Debug, Clone, Deserialize)] @@ -402,15 +402,16 @@ pub struct IngestDocumentParams { pub created_at: Option, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub document_id: Option, + pub document_id: String, } #[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct BatchDocumentItem { pub title: String, pub content: String, pub namespace: String, + pub document_id: String, } #[derive(Debug, Clone, Serialize)] diff --git a/packages/sdk-rust/tests/client_test.rs b/packages/sdk-rust/tests/client_test.rs index 6494240..b31ae49 100644 --- a/packages/sdk-rust/tests/client_test.rs +++ b/packages/sdk-rust/tests/client_test.rs @@ -35,6 +35,15 @@ async fn insert_memory_validates_required() { ..Default::default() }; assert!(client.insert_memory(empty_ns).await.is_err()); + + let empty_doc_id = InsertMemoryParams { + title: "t".into(), + content: "c".into(), + namespace: "ns".into(), + document_id: String::new(), + ..Default::default() + }; + assert!(client.insert_memory(empty_doc_id).await.is_err()); } #[tokio::test] @@ -56,6 +65,7 @@ async fn insert_memory_posts_correctly() { title: "Doc".into(), content: "Content".into(), namespace: "default".into(), + document_id: "doc-1".into(), ..Default::default() }; let res = client.insert_memory(params).await.unwrap(); @@ -193,6 +203,7 @@ async fn api_error_returns_tinyhumans_error() { title: "T".into(), content: "C".into(), namespace: "ns".into(), + document_id: "doc-1".into(), ..Default::default() }; let err = client.insert_memory(params).await.unwrap_err();