Files
2026-07-13 13:12:33 +08:00

82 lines
2.8 KiB
Python

"""Opt-in live LLM smoke.
This is intentionally the only live LLM smoke restored for open-source
readiness. It skips unless credentials are explicitly present.
"""
from __future__ import annotations
import os
import pytest
from opensquilla.provider.openai import OpenAIProvider
from opensquilla.provider.types import ChatConfig, DoneEvent, ErrorEvent, Message, TextDeltaEvent
pytestmark = [pytest.mark.llm, pytest.mark.llm_smoke]
_EXPECTED_TOKEN = "opensquilla-live-smoke-ok"
@pytest.mark.asyncio
async def test_openrouter_live_smoke_returns_expected_token() -> None:
api_key = os.environ.get("OPENROUTER_API_KEY")
if not api_key:
pytest.skip("OPENROUTER_API_KEY not set")
provider = OpenAIProvider(
api_key=api_key,
model=os.environ.get("LLM_TEST_MODEL", "openai/gpt-4o-mini"),
base_url=os.environ.get("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1"),
provider_kind="openrouter",
)
text_parts: list[str] = []
done = False
async for event in provider.chat(
[Message(role="user", content=f"Reply with exactly {_EXPECTED_TOKEN}.")],
config=ChatConfig(max_tokens=32, temperature=0.0, timeout=45.0),
):
if isinstance(event, ErrorEvent):
pytest.fail(f"live LLM smoke failed: {event.code} {event.message}")
if isinstance(event, TextDeltaEvent):
text_parts.append(event.text)
if isinstance(event, DoneEvent):
done = True
assert done is True
assert _EXPECTED_TOKEN in "".join(text_parts).strip().lower()
@pytest.mark.asyncio
async def test_tokenrhythm_live_smoke_returns_expected_token() -> None:
api_key = os.environ.get("TOKENRHYTHM_API_KEY")
if not api_key:
pytest.skip("TOKENRHYTHM_API_KEY not set")
provider = OpenAIProvider(
api_key=api_key,
model=os.environ.get("TOKENRHYTHM_MODEL", "deepseek-v4-flash"),
base_url=os.environ.get("TOKENRHYTHM_BASE_URL", "https://tokenrhythm.studio/v1"),
provider_kind="tokenrhythm",
)
text_parts: list[str] = []
done = False
async for event in provider.chat(
[Message(role="user", content=f"Reply with exactly {_EXPECTED_TOKEN}.")],
# Every TokenRhythm model spends reasoning_content tokens out of
# max_tokens before any text; a small budget returns empty content
# with finish_reason "length".
config=ChatConfig(max_tokens=1024, temperature=0.0, timeout=90.0),
):
if isinstance(event, ErrorEvent):
pytest.fail(f"live LLM smoke failed: {event.code} {event.message}")
if isinstance(event, TextDeltaEvent):
text_parts.append(event.text)
if isinstance(event, DoneEvent):
done = True
assert done is True
assert _EXPECTED_TOKEN in "".join(text_parts).strip().lower()