Spaces:
Sleeping
Sleeping
| """Small mocked checks for advisory auxiliary routing and deterministic safety.""" | |
| import asyncio | |
| import json | |
| import os | |
| import sys | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) | |
| import external_ai.advisory as advisory | |
| import external_ai.advisory_schema as advisory_schema | |
| import trading.trade_cycle as trade_cycle | |
| def _ok(): | |
| return { | |
| "market_bias": "neutral", | |
| "confidence": 0.4, | |
| "summary": "mock advisory", | |
| "supporting_points": ["context"], | |
| "risk_warnings": ["not a signal"], | |
| }, "hermes_auxiliary", "mock-model", None, 12.5 | |
| async def _fake_invoke(prompt): | |
| return _ok() | |
| def test_auxiliary_task_is_primary_provider(monkeypatch): | |
| calls = [] | |
| async def fake(prompt): | |
| calls.append(prompt) | |
| return _ok() | |
| monkeypatch.setattr(advisory, "_invoke_auxiliary_advisory", fake) | |
| monkeypatch.setenv("EXTERNAL_AI_ENABLED", "true") | |
| result = asyncio.run(advisory.get_advisory({"noTradeGuard": True})) | |
| assert calls | |
| assert result["provider"] == "hermes_auxiliary" | |
| assert result["provider_order"] == [advisory.AUXILIARY_TASK] | |
| assert "mock-model" in json.dumps(result) | |
| def test_auxiliary_success_is_structured(monkeypatch): | |
| monkeypatch.setattr(advisory, "_invoke_auxiliary_advisory", _fake_invoke) | |
| monkeypatch.setenv("EXTERNAL_AI_ENABLED", "true") | |
| result = asyncio.run(advisory.get_advisory({})) | |
| assert result["status"] == "available" | |
| assert result["market_bias"] == "neutral" | |
| assert result["confidence"] == 0.4 | |
| assert result["advisory_id"] | |
| def test_auxiliary_invalid_payload_returns_unavailable(monkeypatch): | |
| async def fake(prompt): | |
| return None, "hermes_auxiliary", "mock-model", "invalid_advisory", 1.0 | |
| monkeypatch.setattr(advisory, "_invoke_auxiliary_advisory", fake) | |
| monkeypatch.setenv("EXTERNAL_AI_ENABLED", "true") | |
| result = asyncio.run(advisory.get_advisory({})) | |
| assert result["provider"] == "external_ai_unavailable" | |
| assert result["status"] == "unavailable" | |
| assert result["market_bias"] == "unknown" | |
| def test_auxiliary_timeout_returns_unavailable(monkeypatch): | |
| async def fake(prompt): | |
| await asyncio.sleep(2) | |
| return _ok() | |
| monkeypatch.setattr(advisory, "_invoke_auxiliary_advisory", fake) | |
| monkeypatch.setenv("EXTERNAL_AI_ENABLED", "true") | |
| monkeypatch.setenv("EXTERNAL_AI_TOTAL_TIMEOUT_SECONDS", "1") | |
| result = asyncio.run(advisory.get_advisory({})) | |
| assert result["provider"] == "external_ai_unavailable" | |
| assert result["status"] == "unavailable" | |
| assert result["attempts"][0]["error"] == "timeout" | |
| def test_auxiliary_unavailable_when_runtime_missing(monkeypatch): | |
| async def fake(prompt): | |
| return None, "external_ai_unavailable", "", "hermes_runtime_unavailable", 0.0 | |
| monkeypatch.setattr(advisory, "_invoke_auxiliary_advisory", fake) | |
| monkeypatch.setenv("EXTERNAL_AI_ENABLED", "true") | |
| result = asyncio.run(advisory.get_advisory({"noTradeGuard": True})) | |
| assert result["provider"] == "external_ai_unavailable" | |
| assert result["market_bias"] == "unknown" | |
| assert result["confidence"] == 0.0 | |
| def test_disabled_advisory_returns_safe_result(monkeypatch): | |
| monkeypatch.setenv("EXTERNAL_AI_ENABLED", "false") | |
| result = asyncio.run(advisory.get_advisory({})) | |
| assert result["error"] == "disabled" | |
| assert result["market_bias"] == "unknown" | |
| def test_advisory_cannot_clear_ds4_no_trade_guard(monkeypatch): | |
| async def context(symbol): | |
| return { | |
| "symbol": symbol, | |
| "primary": {"dataState": "PARTIAL"}, | |
| "merged": {}, | |
| "sources": {}, | |
| "warnings": [], | |
| "noTradeGuard": True, | |
| "noTradeReasons": ["DS4 guard"], | |
| } | |
| async def fake_advisory(snapshot): | |
| return { | |
| "provider": "hermes_auxiliary", | |
| "model": "mock", | |
| "market_bias": "bullish", | |
| "confidence": 1.0, | |
| "summary": "bullish", | |
| "supporting_points": [], | |
| "risk_warnings": [], | |
| "latency_ms": 1, | |
| "fallback_used": False, | |
| "error": None, | |
| } | |
| monkeypatch.setattr(trade_cycle, "get_market_context", context) | |
| monkeypatch.setattr(trade_cycle, "get_advisory", fake_advisory) | |
| result = asyncio.run( | |
| trade_cycle.run_futures_cycle("BTCUSDT", execute=False, include_external_context=True) | |
| ) | |
| assert result["decision"] == "NO_TRADE" | |
| assert result["external_advisory"]["market_bias"] == "bullish" | |
| assert result["risk_approved"] is False | |
| def test_advisory_parser_accepts_json_wrapped_in_provider_preamble(): | |
| parsed = advisory_schema.parse_advisory_payload( | |
| "Here is the requested JSON:\n" | |
| '{"market_bias":"neutral","confidence":0.5,"summary":"Mixed",' | |
| '"supporting_points":["Momentum is mixed"],"risk_warnings":["No clear direction"]}' | |
| "\nEnd of response." | |
| ) | |
| assert parsed is not None | |
| assert parsed["market_bias"] == "neutral" | |
| assert parsed["confidence"] == 0.5 | |
| def test_skill_parse_script_roundtrip(): | |
| import subprocess | |
| script = os.path.join( | |
| os.path.dirname(__file__), | |
| "..", | |
| "skills", | |
| "futures", | |
| "market-context-advisory", | |
| "scripts", | |
| "parse_advisory_response.py", | |
| ) | |
| payload = ( | |
| '{"market_bias":"bearish","confidence":0.7,"summary":"Weak",' | |
| '"supporting_points":["Funding elevated"],"risk_warnings":["Volatility"]}' | |
| ) | |
| completed = subprocess.run( | |
| [sys.executable, script, payload], | |
| capture_output=True, | |
| text=True, | |
| check=False, | |
| ) | |
| assert completed.returncode == 0 | |
| parsed = json.loads(completed.stdout) | |
| assert parsed["market_bias"] == "bearish" | |