Spaces:
Running
Running
feat: overhaul MCP architecture with structured tool schemas, comprehensive care-mode skill definitions, and enhanced test coverage for pipelines and service integration.
79df050 | import logging | |
| import json | |
| import pytest | |
| from core import memory_system | |
| async def test_memory_manager_skips_ai_analysis_for_transient_market_query(monkeypatch): | |
| class FailingAnalyzer: | |
| async def analyze_conversation(self, *args, **kwargs): | |
| raise AssertionError("AI memory analysis should be skipped for transient queries") | |
| manager = memory_system.MemoryManager() | |
| manager.analyzer = FailingAnalyzer() | |
| monkeypatch.setattr(memory_system, "_get_memory_client", lambda: object()) | |
| monkeypatch.setattr(memory_system, "db_available", False) | |
| result = await manager.process_conversation( | |
| user_id="u1", | |
| user_message="今天台積電股價收盤價多少?", | |
| assistant_response="查詢中。", | |
| conversation_history=[], | |
| ) | |
| assert result["extracted_memories"] == 0 | |
| assert result["saved_memories"] == 0 | |
| assert result["errors"] == [] | |
| async def test_memory_analyzer_quietly_degrades_on_transient_upstream_error(monkeypatch, caplog): | |
| class Responses: | |
| def create(self, **kwargs): | |
| raise Exception("Error code: 502 - {'error': {'message': 'Upstream request failed'}}") | |
| class Client: | |
| responses = Responses() | |
| class Settings: | |
| OPENAI_USE_RESPONSES = True | |
| GPT_INTENT_MODEL = "gpt-5.4" | |
| OPENAI_MODEL = "gpt-5.4" | |
| OPENAI_TIMEOUT = 30 | |
| monkeypatch.setattr(memory_system, "_get_memory_client", lambda: Client()) | |
| monkeypatch.setattr(memory_system, "settings", Settings) | |
| analyzer = memory_system.MemoryAnalyzer() | |
| with caplog.at_level(logging.ERROR): | |
| memories = await analyzer.analyze_conversation( | |
| user_message="我喜歡安靜的咖啡店", | |
| assistant_response="我記住了。", | |
| conversation_history=[], | |
| ) | |
| assert memories == [] | |
| assert "AI記憶分析時發生錯誤" not in caplog.text | |
| def test_memory_analyzer_uses_structured_responses_payload(monkeypatch): | |
| captured = {} | |
| class Responses: | |
| def create(self, **kwargs): | |
| captured.update(kwargs) | |
| class Response: | |
| output_text = '{"memories":[]}' | |
| output = [] | |
| return Response() | |
| class Client: | |
| responses = Responses() | |
| class Settings: | |
| OPENAI_USE_RESPONSES = True | |
| GPT_INTENT_MODEL = "gpt-5.4-mini" | |
| OPENAI_MODEL = "gpt-5.4-mini" | |
| monkeypatch.setattr(memory_system, "settings", Settings) | |
| analyzer = memory_system.MemoryAnalyzer() | |
| response = analyzer._create_analysis_response( | |
| Client(), | |
| [{"role": "system", "content": "JSON"}, {"role": "user", "content": "JSON"}], | |
| 500, | |
| ) | |
| assert response.output_text == '{"memories":[]}' | |
| assert captured["model"] == "gpt-5.4-mini" | |
| assert captured["max_output_tokens"] == 500 | |
| assert captured["store"] is False | |
| assert captured["text"]["format"]["type"] == "json_schema" | |
| assert captured["text"]["format"]["strict"] is True | |
| assert captured["text"]["format"]["schema"]["required"] == ["memories"] | |
| assert "reasoning" not in captured | |
| async def test_memory_analyzer_retries_transient_upstream_error_then_succeeds(monkeypatch): | |
| calls = {"count": 0} | |
| class Responses: | |
| def create(self, **kwargs): | |
| calls["count"] += 1 | |
| if calls["count"] == 1: | |
| raise Exception("502 upstream request failed") | |
| class Response: | |
| output_text = json.dumps( | |
| { | |
| "memories": [ | |
| { | |
| "type": "preferences", | |
| "content": "使用者喜歡安靜的咖啡店", | |
| "importance": 0.8, | |
| } | |
| ] | |
| }, | |
| ensure_ascii=False, | |
| ) | |
| output = [] | |
| return Response() | |
| class Client: | |
| responses = Responses() | |
| class Settings: | |
| OPENAI_USE_RESPONSES = True | |
| GPT_INTENT_MODEL = "gpt-5.4-mini" | |
| OPENAI_MODEL = "gpt-5.4-mini" | |
| OPENAI_TIMEOUT = 30 | |
| monkeypatch.setattr(memory_system, "_get_memory_client", lambda: Client()) | |
| monkeypatch.setattr(memory_system, "settings", Settings) | |
| monkeypatch.setattr(memory_system.MemoryAnalyzer, "_transient_backoff", staticmethod(lambda attempt: _noop())) | |
| analyzer = memory_system.MemoryAnalyzer() | |
| memories = await analyzer.analyze_conversation("我喜歡安靜的咖啡店", "我記住了。", []) | |
| assert calls["count"] == 2 | |
| assert memories[0]["type"] == "preferences" | |
| assert memories[0]["source"] == "ai_analysis" | |
| async def _noop(): | |
| return None | |