diff options
| author | CodeWithShreyans <[email protected]> | 2025-08-27 23:34:49 +0000 |
|---|---|---|
| committer | CodeWithShreyans <[email protected]> | 2025-08-27 23:34:49 +0000 |
| commit | 3a0e264b7eb18fe3b6d2de25e79879ada7c9f3ec (patch) | |
| tree | 4f2be5731860abece575bf4946ef881972502d73 /packages/openai-sdk-python/tests | |
| parent | feat: support project-specific installation commands (#390) (diff) | |
| download | supermemory-3a0e264b7eb18fe3b6d2de25e79879ada7c9f3ec.tar.xz supermemory-3a0e264b7eb18fe3b6d2de25e79879ada7c9f3ec.zip | |
feat: openai js and python sdk utilities (#389)shreyans/08-27-feat_openai_js_and_python_sdk_utilities
needs testing
Diffstat (limited to 'packages/openai-sdk-python/tests')
| -rw-r--r-- | packages/openai-sdk-python/tests/__init__.py | 1 | ||||
| -rw-r--r-- | packages/openai-sdk-python/tests/test_infinite_chat.py | 387 | ||||
| -rw-r--r-- | packages/openai-sdk-python/tests/test_tools.py | 350 |
3 files changed, 738 insertions, 0 deletions
diff --git a/packages/openai-sdk-python/tests/__init__.py b/packages/openai-sdk-python/tests/__init__.py new file mode 100644 index 00000000..66173aec --- /dev/null +++ b/packages/openai-sdk-python/tests/__init__.py @@ -0,0 +1 @@ +# Test package diff --git a/packages/openai-sdk-python/tests/test_infinite_chat.py b/packages/openai-sdk-python/tests/test_infinite_chat.py new file mode 100644 index 00000000..9fdf52c5 --- /dev/null +++ b/packages/openai-sdk-python/tests/test_infinite_chat.py @@ -0,0 +1,387 @@ +"""Tests for infinite_chat module.""" + +import os +import pytest +from typing import List + +from openai.types.chat import ChatCompletionMessageParam +from ..src import ( + SupermemoryOpenAI, + SupermemoryInfiniteChatConfigWithProviderName, + SupermemoryInfiniteChatConfigWithProviderUrl, + ProviderName, +) + + +# Test configuration +PROVIDERS: List[ProviderName] = [ + "openai", + "anthropic", + "openrouter", + "deepinfra", + "groq", + "google", + "cloudflare", +] + + +def test_api_key() -> str: + """Get test Supermemory API key from environment.""" + api_key = os.getenv("SUPERMEMORY_API_KEY") + if not api_key: + pytest.skip("SUPERMEMORY_API_KEY environment variable is required for tests") + return api_key + + +def test_provider_api_key() -> str: + """Get test provider API key from environment.""" + api_key = os.getenv("PROVIDER_API_KEY") + if not api_key: + pytest.skip("PROVIDER_API_KEY environment variable is required for tests") + return api_key + + +def test_provider_name() -> ProviderName: + """Get test provider name from environment.""" + provider_name = os.getenv("PROVIDER_NAME", "openai") + if provider_name not in PROVIDERS: + pytest.fail(f"Invalid provider name: {provider_name}") + return provider_name # type: ignore + + +def test_provider_url() -> str: + """Get test provider URL from environment.""" + return os.getenv("PROVIDER_URL", "") + + +def test_model_name() -> str: + """Get test model name from environment.""" + return os.getenv("MODEL_NAME", "gpt-4o-mini") + + +def test_headers() -> dict: + """Get test headers.""" + return {"custom-header": "test-value"} + + +def test_messages() -> List[List[ChatCompletionMessageParam]]: + """Test message sets.""" + return [ + [{"role": "user", "content": "Hello!"}], + [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is AI?"}, + ], + [ + {"role": "user", "content": "Tell me a joke"}, + { + "role": "assistant", + "content": "Why don't scientists trust atoms? Because they make up everything!", + }, + {"role": "user", "content": "Tell me another one"}, + ], + ] + + +class TestClientCreation: + """Test client creation.""" + + def test_create_client_with_provider_name( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + test_headers: dict, + ): + """Test creating client with provider name configuration.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + headers=test_headers, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + assert client is not None + assert client.chat is not None + + def test_create_client_with_openai_provider( + self, test_api_key: str, test_provider_api_key: str, test_headers: dict + ): + """Test creating client with OpenAI provider configuration.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name="openai", + provider_api_key=test_provider_api_key, + headers=test_headers, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + assert client is not None + + def test_create_client_with_custom_provider_url( + self, test_api_key: str, test_provider_api_key: str, test_headers: dict + ): + """Test creating client with custom provider URL.""" + custom_url = "https://custom-provider.com/v1" + config = SupermemoryInfiniteChatConfigWithProviderUrl( + provider_url=custom_url, + provider_api_key=test_provider_api_key, + headers=test_headers, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + assert client is not None + + +class TestChatCompletions: + """Test chat completions functionality.""" + + @pytest.mark.asyncio + async def test_create_chat_completion_simple_message( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + test_model_name: str, + test_messages: List[List[ChatCompletionMessageParam]], + ): + """Test creating chat completion with simple message.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + headers={}, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + result = await client.create_chat_completion( + model=test_model_name, + messages=test_messages[0], # "Hello!" + ) + + assert result is not None + assert hasattr(result, "choices") + assert len(result.choices) > 0 + assert result.choices[0].message.content is not None + + @pytest.mark.asyncio + async def test_chat_completion_convenience_method( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + test_model_name: str, + test_messages: List[List[ChatCompletionMessageParam]], + ): + """Test chat completion using convenience method.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + headers={}, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + result = await client.chat_completion( + messages=test_messages[1], # System + user messages + model=test_model_name, + temperature=0.7, + ) + + assert result is not None + assert hasattr(result, "choices") + assert len(result.choices) > 0 + assert result.choices[0].message.content is not None + + @pytest.mark.asyncio + async def test_handle_conversation_history( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + test_model_name: str, + test_messages: List[List[ChatCompletionMessageParam]], + ): + """Test handling conversation history.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + headers={}, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + result = await client.chat_completion( + messages=test_messages[2], # Multi-turn conversation + model=test_model_name, + ) + + assert result is not None + assert hasattr(result, "choices") + assert len(result.choices) > 0 + assert result.choices[0].message.content is not None + + @pytest.mark.asyncio + async def test_custom_headers( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + test_model_name: str, + ): + """Test working with custom headers.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + headers={"x-custom-header": "test-value"}, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + result = await client.chat_completion( + messages=[{"role": "user", "content": "Hello"}], + model=test_model_name, + ) + + assert result is not None + assert hasattr(result, "choices") + + +class TestConfigurationValidation: + """Test configuration validation.""" + + def test_handle_empty_headers_object( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + ): + """Test handling empty headers object.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + headers={}, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + assert client is not None + + def test_handle_configuration_without_headers( + self, + test_api_key: str, + test_provider_api_key: str, + test_provider_name: ProviderName, + ): + """Test handling configuration without headers.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name=test_provider_name, + provider_api_key=test_provider_api_key, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + assert client is not None + + def test_handle_different_api_keys(self): + """Test handling different API keys.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name="openai", + provider_api_key="different-provider-key", + ) + + client = SupermemoryOpenAI("different-sm-key", config) + + assert client is not None + + +class TestDisabledEndpoints: + """Test that non-chat endpoints are disabled.""" + + def test_disabled_endpoints_throw_errors( + self, test_api_key: str, test_provider_api_key: str + ): + """Test that all disabled endpoints throw appropriate errors.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name="openai", + provider_api_key=test_provider_api_key, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + # Test that all disabled endpoints throw appropriate errors + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.embeddings + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.fine_tuning + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.images + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.audio + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.models + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.moderations + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.files + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.batches + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.uploads + + with pytest.raises( + RuntimeError, match="Supermemory only supports chat completions" + ): + _ = client.beta + + def test_chat_completions_still_work( + self, test_api_key: str, test_provider_api_key: str + ): + """Test that chat completions still work after disabling other endpoints.""" + config = SupermemoryInfiniteChatConfigWithProviderName( + provider_name="openai", + provider_api_key=test_provider_api_key, + ) + + client = SupermemoryOpenAI(test_api_key, config) + + # Chat completions should still be accessible + assert client.chat is not None + assert client.chat.completions is not None + assert callable(client.create_chat_completion) + assert callable(client.chat_completion) diff --git a/packages/openai-sdk-python/tests/test_tools.py b/packages/openai-sdk-python/tests/test_tools.py new file mode 100644 index 00000000..835622c7 --- /dev/null +++ b/packages/openai-sdk-python/tests/test_tools.py @@ -0,0 +1,350 @@ +"""Tests for tools module.""" + +import os +import pytest +import json +from typing import List + +from openai.types.chat import ChatCompletionMessageToolCall +from ..src import ( + SupermemoryTools, + SupermemoryToolsConfig, + SupermemoryOpenAI, + SupermemoryInfiniteChatConfigWithProviderName, + create_supermemory_tools, + get_memory_tool_definitions, + execute_memory_tool_calls, + create_search_memories_tool, + create_add_memory_tool, +) + + +def test_api_key() -> str: + """Get test Supermemory API key from environment.""" + api_key = os.getenv("SUPERMEMORY_API_KEY") + if not api_key: + pytest.skip("SUPERMEMORY_API_KEY environment variable is required for tests") + return api_key + + +def test_provider_api_key() -> str: + """Get test provider API key from environment.""" + api_key = os.getenv("PROVIDER_API_KEY") + if not api_key: + pytest.skip("PROVIDER_API_KEY environment variable is required for tests") + return api_key + + +def test_base_url() -> str: + """Get test base URL from environment.""" + return os.getenv("SUPERMEMORY_BASE_URL", "") + + +def test_model_name() -> str: + """Get test model name from environment.""" + return os.getenv("MODEL_NAME", "gpt-4o-mini") + + +class TestToolInitialization: + """Test tool initialization.""" + + def test_create_tools_with_default_configuration(self, test_api_key: str): + """Test creating tools with default configuration.""" + config: SupermemoryToolsConfig = {} + tools = SupermemoryTools(test_api_key, config) + + assert tools is not None + assert tools.get_tool_definitions() is not None + assert len(tools.get_tool_definitions()) == 3 + + def test_create_tools_with_helper(self, test_api_key: str): + """Test creating tools with createSupermemoryTools helper.""" + tools = create_supermemory_tools( + test_api_key, + { + "project_id": "test-project", + }, + ) + + assert tools is not None + assert tools.get_tool_definitions() is not None + + def test_create_tools_with_custom_base_url( + self, test_api_key: str, test_base_url: str + ): + """Test creating tools with custom baseUrl.""" + if not test_base_url: + pytest.skip("SUPERMEMORY_BASE_URL not provided") + + config: SupermemoryToolsConfig = { + "base_url": test_base_url, + } + tools = SupermemoryTools(test_api_key, config) + + assert tools is not None + assert len(tools.get_tool_definitions()) == 3 + + def test_create_tools_with_project_id(self, test_api_key: str): + """Test creating tools with projectId configuration.""" + config: SupermemoryToolsConfig = { + "project_id": "test-project-123", + } + tools = SupermemoryTools(test_api_key, config) + + assert tools is not None + assert len(tools.get_tool_definitions()) == 3 + + def test_create_tools_with_custom_container_tags(self, test_api_key: str): + """Test creating tools with custom container tags.""" + config: SupermemoryToolsConfig = { + "container_tags": ["custom-tag-1", "custom-tag-2"], + } + tools = SupermemoryTools(test_api_key, config) + + assert tools is not None + assert len(tools.get_tool_definitions()) == 3 + + +class TestToolDefinitions: + """Test tool definitions.""" + + def test_return_proper_openai_function_definitions(self): + """Test returning proper OpenAI function definitions.""" + definitions = get_memory_tool_definitions() + + assert definitions is not None + assert len(definitions) == 3 + + # Check searchMemories + search_tool = next( + (d for d in definitions if d["function"]["name"] == "search_memories"), None + ) + assert search_tool is not None + assert search_tool["type"] == "function" + assert "information_to_get" in search_tool["function"]["parameters"]["required"] + + # Check addMemory + add_tool = next( + (d for d in definitions if d["function"]["name"] == "add_memory"), None + ) + assert add_tool is not None + assert add_tool["type"] == "function" + assert "memory" in add_tool["function"]["parameters"]["required"] + + def test_consistent_tool_definitions_from_class_and_helper(self, test_api_key: str): + """Test that tool definitions are consistent between class and helper.""" + tools = SupermemoryTools(test_api_key) + class_definitions = tools.get_tool_definitions() + helper_definitions = get_memory_tool_definitions() + + assert class_definitions == helper_definitions + + +class TestMemoryOperations: + """Test memory operations.""" + + @pytest.mark.asyncio + async def test_search_memories(self, test_api_key: str, test_base_url: str): + """Test searching memories.""" + config: SupermemoryToolsConfig = { + "project_id": "test-search", + } + if test_base_url: + config["base_url"] = test_base_url + + tools = SupermemoryTools(test_api_key, config) + + result = await tools.search_memories( + information_to_get="test preferences", + limit=5, + ) + + assert result is not None + assert "success" in result + assert isinstance(result["success"], bool) + + if result["success"]: + assert "results" in result + assert "count" in result + assert isinstance(result["count"], int) + else: + assert "error" in result + + @pytest.mark.asyncio + async def test_add_memory(self, test_api_key: str, test_base_url: str): + """Test adding memory.""" + config: SupermemoryToolsConfig = { + "container_tags": ["test-add-memory"], + } + if test_base_url: + config["base_url"] = test_base_url + + tools = SupermemoryTools(test_api_key, config) + + result = await tools.add_memory( + memory="User prefers dark roast coffee in the morning - test memory" + ) + + assert result is not None + assert "success" in result + assert isinstance(result["success"], bool) + + if result["success"]: + assert "memory" in result + else: + assert "error" in result + + +class TestIndividualToolCreators: + """Test individual tool creators.""" + + def test_create_individual_search_tool(self, test_api_key: str): + """Test creating individual search tool.""" + search_tool = create_search_memories_tool( + test_api_key, + { + "project_id": "test-individual", + }, + ) + + assert search_tool is not None + assert search_tool.definition is not None + assert callable(search_tool.execute) + assert search_tool.definition["function"]["name"] == "search_memories" + + def test_create_individual_add_tool(self, test_api_key: str): + """Test creating individual add tool.""" + add_tool = create_add_memory_tool( + test_api_key, + { + "project_id": "test-individual", + }, + ) + + assert add_tool is not None + assert add_tool.definition is not None + assert callable(add_tool.execute) + assert add_tool.definition["function"]["name"] == "add_memory" + + +class TestOpenAIIntegration: + """Test OpenAI integration.""" + + @pytest.mark.asyncio + async def test_work_with_supermemory_openai_for_function_calling( + self, + test_api_key: str, + test_provider_api_key: str, + test_model_name: str, + test_base_url: str, + ): + """Test working with SupermemoryOpenAI for function calling.""" + client = SupermemoryOpenAI( + test_api_key, + SupermemoryInfiniteChatConfigWithProviderName( + provider_name="openai", + provider_api_key=test_provider_api_key, + ), + ) + + tools_config: SupermemoryToolsConfig = { + "project_id": "test-openai-integration", + } + if test_base_url: + tools_config["base_url"] = test_base_url + + tools = SupermemoryTools(test_api_key, tools_config) + + response = await client.chat_completion( + messages=[ + { + "role": "system", + "content": ( + "You are a helpful assistant with access to user memories. " + "When the user asks you to remember something, use the add_memory tool." + ), + }, + { + "role": "user", + "content": "Please remember that I prefer tea over coffee", + }, + ], + model=test_model_name, + tools=tools.get_tool_definitions(), + ) + + assert response is not None + assert hasattr(response, "choices") + + choice = response.choices[0] + assert choice.message is not None + + # If the model decided to use function calling, test the execution + if hasattr(choice.message, "tool_calls") and choice.message.tool_calls: + tool_results = await execute_memory_tool_calls( + test_api_key, + choice.message.tool_calls, + tools_config, + ) + + assert tool_results is not None + assert len(tool_results) == len(choice.message.tool_calls) + + for result in tool_results: + assert result["role"] == "tool" + assert "content" in result + assert "tool_call_id" in result + + @pytest.mark.asyncio + async def test_handle_multiple_tool_calls( + self, test_api_key: str, test_base_url: str + ): + """Test handling multiple tool calls.""" + tools_config: SupermemoryToolsConfig = { + "container_tags": ["test-multi-tools"], + } + if test_base_url: + tools_config["base_url"] = test_base_url + + # Simulate tool calls (normally these would come from OpenAI) + mock_tool_calls: List[ChatCompletionMessageToolCall] = [ + ChatCompletionMessageToolCall( + id="call_1", + type="function", + function={ + "name": "search_memories", + "arguments": json.dumps({"information_to_get": "preferences"}), + }, + ), + ChatCompletionMessageToolCall( + id="call_2", + type="function", + function={ + "name": "add_memory", + "arguments": json.dumps( + {"memory": "Test memory for multiple calls"} + ), + }, + ), + ] + + results = await execute_memory_tool_calls( + test_api_key, mock_tool_calls, tools_config + ) + + assert results is not None + assert len(results) == 2 + + assert results[0]["tool_call_id"] == "call_1" + assert results[1]["tool_call_id"] == "call_2" + + for result in results: + assert result["role"] == "tool" + assert "content" in result + + content = json.loads(result["content"]) + assert "success" in content |