diff --git a/README.md b/README.md index 4897243..4c4d893 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A decentralized, chat-first platform that combines **real-time messaging, token payments, and community-driven funding** into a single seamless experience. + + This project reimagines how people coordinate, transact, and build together online by embedding financial actions directly into conversations. Users can send tokens as easily as messages, contribute to shared group treasuries, and fund ideas through lightweight, on-chain proposals—all without leaving the chat interface. Built on blockchain infrastructure and modern messaging protocols, the platform bridges the gap between Web2 usability and Web3 ownership. diff --git a/apps/ai_agent/main.py b/apps/ai_agent/main.py index 1af60a6..fc50598 100644 --- a/apps/ai_agent/main.py +++ b/apps/ai_agent/main.py @@ -8,6 +8,11 @@ from pydantic import BaseModel from weaviate.classes.query import Filter +try: + from openai import OpenAI +except ImportError: # pragma: no cover - exercised when the dependency is absent + OpenAI = None + app = FastAPI(title="AI Agent API") _SYSTEM_PROMPT = ( diff --git a/apps/ai_agent/tests/test_chat.py b/apps/ai_agent/tests/test_chat.py new file mode 100644 index 0000000..37054a0 --- /dev/null +++ b/apps/ai_agent/tests/test_chat.py @@ -0,0 +1,77 @@ +"""Unit tests for POST /chat (issue #144).""" + +import pytest +from unittest.mock import MagicMock + +_BASE_BODY = { + "message": "What is Clicked?", + "conversation_id": "conv-123", +} + + +def _fake_chat_reply(content: str = "This is a test reply."): + msg = MagicMock() + msg.content = content + choice = MagicMock() + choice.message = msg + resp = MagicMock() + resp.choices = [choice] + return resp + + +def test_missing_api_key_returns_500(monkeypatch, client): + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + response = client.post("/chat", json=_BASE_BODY) + assert response.status_code == 500 + + +def test_valid_request_returns_reply(mock_openai, client): + mock_client = mock_openai.return_value + mock_client.chat.completions.create.return_value = _fake_chat_reply( + "Hello from Clicked AI!" + ) + + response = client.post("/chat", json=_BASE_BODY) + assert response.status_code == 200 + data = response.json() + assert "reply" in data + assert data["reply"] == "Hello from Clicked AI!" + + +def test_system_prompt_contains_context(mock_openai, client): + mock_client = mock_openai.return_value + mock_client.chat.completions.create.return_value = _fake_chat_reply("Sure, I can help!") + + response = client.post("/chat", json=_BASE_BODY) + assert response.status_code == 200 + + call_args = mock_client.chat.completions.create.call_args + messages = call_args[1]["messages"] + system_msg = messages[0] + assert system_msg["role"] == "system" + content = system_msg["content"] + assert "Clicked" in content + assert "Stellar" in content + assert "messaging" in content or "payment" in content + + +def test_missing_message_returns_422(client): + body = {"conversation_id": "conv-123"} + response = client.post("/chat", json=body) + assert response.status_code == 422 + + +def test_missing_conversation_id_returns_422(client): + body = {"message": "Hello"} + response = client.post("/chat", json=body) + assert response.status_code == 422 + + +def test_correct_model_used(mock_openai, client): + mock_client = mock_openai.return_value + mock_client.chat.completions.create.return_value = _fake_chat_reply("OK") + + client.post("/chat", json=_BASE_BODY) + + call_args = mock_client.chat.completions.create.call_args + assert call_args[1]["model"] == "gpt-4o-mini"