Skip to content
Merged
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
1 change: 1 addition & 0 deletions packages/sdk-cpp/example/example_usage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions packages/sdk-cpp/include/tinyhumans/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct InsertMemoryParams {
std::optional<std::string> priority;
std::optional<long> created_at;
std::optional<long> updated_at;
std::optional<std::string> 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; }
Expand All @@ -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 {
Expand All @@ -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;
}
};
Expand Down
1 change: 1 addition & 0 deletions packages/sdk-cpp/test/integration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 11 additions & 3 deletions packages/sdk-cpp/test/memory_client_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object?> { ["lang"] = "csharp" },
});
Console.WriteLine($"Success={insertResp.Success} Status={insertResp.Status}");
Expand Down
4 changes: 3 additions & 1 deletion packages/sdk-csharp/src/TinyHumans.Sdk/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object?> ToJsonObject()
Expand All @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object?> { ["source"] = "integration-test" },
CreatedAt = nowSeconds,
UpdatedAt = nowSeconds,
Expand Down
15 changes: 12 additions & 3 deletions packages/sdk-csharp/test/TinyHumans.Sdk.Tests/MemoryClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -77,6 +77,7 @@ public async Task InsertMemory_SendsCorrectRequest()
Title = "t1",
Content = "c1",
Namespace = "ns1",
DocumentId = "doc-1",
});

Assert.True(resp.Success);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -134,6 +135,14 @@ await Assert.ThrowsAsync<ArgumentException>(() =>
client.InsertMemoryAsync(new InsertMemoryParams { Title = "t", Content = "c" }));
}

[Fact]
public async Task InsertMemory_ThrowsOnMissingDocumentId()
{
using var client = CreateClient();
await Assert.ThrowsAsync<ArgumentException>(() =>
client.InsertMemoryAsync(new InsertMemoryParams { Title = "t", Content = "c", Namespace = "ns" }));
}

// ── RecallMemory ──

[Fact]
Expand Down
6 changes: 4 additions & 2 deletions packages/sdk-dart/example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Future<void> 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}');

Expand Down Expand Up @@ -101,6 +102,7 @@ Future<void> main() async {
title: 'Dart Guide',
content: 'Dart SDK usage guide',
namespace: ns,
documentId: 'dart-guide-001',
));
print('InsertDocument: $docResp');
} catch (e) {
Expand All @@ -114,9 +116,9 @@ Future<void> 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');
Expand Down
11 changes: 10 additions & 1 deletion packages/sdk-dart/lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, dynamic> toJson() {
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -460,13 +463,15 @@ class InsertDocumentParams {
final String title;
final String content;
final String namespace;
final String documentId;
final Map<String, dynamic>? metadata;
final String? sourceType;

InsertDocumentParams({
required this.title,
required this.content,
required this.namespace,
required this.documentId,
this.metadata,
this.sourceType,
});
Expand All @@ -481,13 +486,17 @@ class InsertDocumentParams {
if (namespace.trim().isEmpty) {
throw ArgumentError('namespace is required');
}
if (documentId.trim().isEmpty) {
throw ArgumentError('documentId is required');
}
}

Map<String, dynamic> toJson() {
final map = <String, dynamic>{
'title': title,
'content': content,
'namespace': namespace,
'documentId': documentId,
};
if (metadata != null) map['metadata'] = metadata;
if (sourceType != null) map['sourceType'] = sourceType;
Expand Down
6 changes: 4 additions & 2 deletions packages/sdk-dart/test/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand All @@ -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');
Expand Down
30 changes: 28 additions & 2 deletions packages/sdk-dart/test/tinyhumans_memory_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ void main() {
title: 't1',
content: 'c1',
namespace: 'ns1',
documentId: 'doc-1',
));

expect(resp.success, isTrue);
Expand Down Expand Up @@ -139,6 +140,7 @@ void main() {
title: 't',
content: 'c',
namespace: 'ns',
documentId: 'doc-1',
));

expect(resp.usage, isNotNull);
Expand Down Expand Up @@ -170,6 +172,15 @@ void main() {
throwsA(isA<ArgumentError>()),
);
});

test('throws on missing documentId', () {
final client = createClient();
expect(
() => client.insertMemory(
InsertMemoryParams(title: 't', content: 'c', namespace: 'ns')),
throwsA(isA<ArgumentError>()),
);
});
});

// ── RecallMemory ──
Expand Down Expand Up @@ -493,6 +504,7 @@ void main() {
title: 'Doc Title',
content: 'Doc content',
namespace: 'ns',
documentId: 'doc-1',
));

expect(captured!.method, equals('POST'));
Expand All @@ -510,6 +522,20 @@ void main() {
title: '',
content: 'c',
namespace: 'ns',
documentId: 'doc-1',
)),
throwsA(isA<ArgumentError>()),
);
});

test('throws on empty documentId', () {
final client = createClient();
expect(
() => client.insertDocument(InsertDocumentParams(
title: 't',
content: 'c',
namespace: 'ns',
documentId: '',
)),
throwsA(isA<ArgumentError>()),
);
Expand All @@ -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'),
],
));

Expand Down
13 changes: 7 additions & 6 deletions packages/sdk-golang/example/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading