diff --git a/examples/memory/memory.add.py b/examples/memory/memory.add.py new file mode 100644 index 0000000..6de18df --- /dev/null +++ b/examples/memory/memory.add.py @@ -0,0 +1,76 @@ +""" +Example: Add text directly to memory + +1. Run memory.create.py first to create a memory. +2. Add your API key to the environment variables or replace it in the code. +""" + +import os + +from langbase import Langbase + +# Initialize the Langbase client +langbase = Langbase(api_key=os.getenv("LANGBASE_API_KEY")) + + +def main(): + try: + # Basic text addition + print("šŸ“ Adding basic text to memory...") + basic_result = langbase.memories.add( + memory_name="my-knowledge-base", + text="This is important information about machine learning fundamentals. " + "It covers supervised learning, unsupervised learning, and reinforcement learning concepts.", + ) + print(f"āœ… Basic text added: {basic_result['document_name']}") + + # Text addition with custom name and metadata + print("\nšŸ“ Adding detailed text with metadata...") + detailed_result = langbase.memories.add( + memory_name="my-knowledge-base", + text="Deep learning is a subset of machine learning that uses artificial neural networks " + "with multiple layers to model and understand complex patterns in data. It has " + "revolutionized fields like computer vision, natural language processing, and speech recognition.", + document_name="deep-learning-intro", + metadata={ + "category": "machine-learning", + "topic": "deep-learning", + "difficulty": "intermediate", + "source": "manual-entry", + }, + ) + print(f"āœ… Detailed text added: {detailed_result['document_name']}") + + # Multiple text entries + texts = [ + { + "text": "Supervised learning uses labeled training data to learn a mapping from inputs to outputs.", + "document_name": "supervised-learning", + "metadata": {"type": "definition", "category": "ml-concepts"}, + }, + { + "text": "Unsupervised learning finds hidden patterns in data without using labeled examples.", + "document_name": "unsupervised-learning", + "metadata": {"type": "definition", "category": "ml-concepts"}, + }, + { + "text": "Reinforcement learning learns optimal actions through trial and error interactions with an environment.", + "document_name": "reinforcement-learning", + "metadata": {"type": "definition", "category": "ml-concepts"}, + }, + ] + + print("\nšŸ“ Adding multiple texts...") + for item in texts: + result = langbase.memories.add(memory_name="my-knowledge-base", **item) + print(f"āœ… Added: {result['document_name']}") + + print("\nšŸŽ‰ All texts have been added to the memory!") + print("You can now query this memory to retrieve relevant information.") + + except Exception as error: + print(f"āŒ Error: {error}") + + +if __name__ == "__main__": + main() diff --git a/langbase/constants.py b/langbase/constants.py index 223f4b2..f2c8dbf 100644 --- a/langbase/constants.py +++ b/langbase/constants.py @@ -38,6 +38,7 @@ MEMORY_DOCUMENTS_ENDPOINT = "/v1/memory/{memory_name}/documents" MEMORY_DOCUMENT_DETAIL_ENDPOINT = "/v1/memory/{memory_name}/documents/{document_name}" MEMORY_DOCUMENTS_UPLOAD_ENDPOINT = "/v1/memory/documents" +MEMORY_TEXT_ADD_ENDPOINT = "/v1/memory/text" MEMORY_DOCUMENT_EMBEDDINGS_RETRY_ENDPOINT = ( "/v1/memory/{memory_name}/documents/{document_name}/embeddings/retry" ) diff --git a/langbase/primitives/memories.py b/langbase/primitives/memories.py index 473cc71..1b6ef73 100644 --- a/langbase/primitives/memories.py +++ b/langbase/primitives/memories.py @@ -17,11 +17,13 @@ MEMORY_DOCUMENTS_UPLOAD_ENDPOINT, MEMORY_ENDPOINT, MEMORY_RETRIEVE_ENDPOINT, + MEMORY_TEXT_ADD_ENDPOINT, ) from langbase.errors import APIError, create_api_error from langbase.types import ( ContentType, EmbeddingModel, + MemoryAddTextResponse, MemoryCreateResponse, MemoryDeleteDocResponse, MemoryDeleteResponse, @@ -250,3 +252,30 @@ def list(self) -> List[MemoryListResponse]: List of memory objects """ return self.request.get(MEMORY_ENDPOINT) + + def add( + self, + memory_name: str, + text: str, + document_name: Optional[str] = None, + metadata: Optional[Dict[str, str]] = None, + ) -> MemoryAddTextResponse: + """ + Add text directly to a memory without file upload. + + Args: + memory_name: Name of the memory to add text to + text: Text content to add to the memory + document_name: Optional custom document name + metadata: Optional metadata for the text document + + Returns: + Text addition response + """ + options = { + "memoryName": memory_name, + "text": text, + "documentName": document_name, + "metadata": metadata, + } + return self.request.post(MEMORY_TEXT_ADD_ENDPOINT, clean_null_values(options)) diff --git a/langbase/types.py b/langbase/types.py index c98f6de..f31d462 100644 --- a/langbase/types.py +++ b/langbase/types.py @@ -371,6 +371,15 @@ class MemoryRetryDocEmbedResponse(BaseDeleteResponse): pass +class MemoryAddTextResponse(TypedDict): + """Response from adding text to memory.""" + + document_name: str + status: Literal["queued"] + memory_name: str + url: str + + class MemoryRetrieveResponse(TypedDict): """Response from retrieving from memory.""" diff --git a/tests/test_memories.py b/tests/test_memories.py index e6f1e6c..56935b7 100644 --- a/tests/test_memories.py +++ b/tests/test_memories.py @@ -15,8 +15,10 @@ MEMORY_DOCUMENTS_UPLOAD_ENDPOINT, MEMORY_ENDPOINT, MEMORY_RETRIEVE_ENDPOINT, + MEMORY_TEXT_ADD_ENDPOINT, ) from langbase.types import ( + MemoryAddTextResponse, MemoryCreateResponse, MemoryDeleteResponse, MemoryListDocResponse, @@ -265,3 +267,76 @@ def test_documents_embeddings_retry(self, langbase_client, mock_responses): assert len(responses.calls) == 1 request = responses.calls[0].request validate_response_headers(request.headers, AUTH_AND_JSON_CONTENT_HEADER) + + @responses.activate + def test_memories_add_text_minimal(self, langbase_client): + """Test memories.add method with minimal parameters.""" + memory_name = "test-memory" + text = "This is a test text to add to memory." + + expected_response = { + "document_name": "text-2024-08-21T10-30-45.txt", + "status": "queued", + "memory_name": memory_name, + "url": f"https://langbase.com/memory/user/{memory_name}", + } + + responses.add( + responses.POST, + f"{BASE_URL}{MEMORY_TEXT_ADD_ENDPOINT}", + json=expected_response, + status=200, + ) + + result = langbase_client.memories.add(memory_name=memory_name, text=text) + + assert result == expected_response + assert len(responses.calls) == 1 + request = responses.calls[0].request + validate_response_headers(request.headers, AUTH_AND_JSON_CONTENT_HEADER) + + request_json = json.loads(request.body) + assert request_json["memoryName"] == memory_name + assert request_json["text"] == text + assert "documentName" not in request_json + assert "metadata" not in request_json + + @responses.activate + def test_memories_add_text_full(self, langbase_client): + """Test memories.add method with all parameters.""" + memory_name = "test-memory" + text = "This is a comprehensive test text with metadata." + document_name = "test-document" + metadata = {"category": "test", "source": "unit-test", "difficulty": "easy"} + + expected_response = { + "document_name": f"{document_name}.txt", + "status": "queued", + "memory_name": memory_name, + "url": f"https://langbase.com/memory/user/{memory_name}", + } + + responses.add( + responses.POST, + f"{BASE_URL}{MEMORY_TEXT_ADD_ENDPOINT}", + json=expected_response, + status=200, + ) + + result = langbase_client.memories.add( + memory_name=memory_name, + text=text, + document_name=document_name, + metadata=metadata, + ) + + assert result == expected_response + assert len(responses.calls) == 1 + request = responses.calls[0].request + validate_response_headers(request.headers, AUTH_AND_JSON_CONTENT_HEADER) + + request_json = json.loads(request.body) + assert request_json["memoryName"] == memory_name + assert request_json["text"] == text + assert request_json["documentName"] == document_name + assert request_json["metadata"] == metadata