CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes45downloads
smoke_real.py389 linesDownload Raw Back to scripts
1#!/usr/bin/env python32"""Run real end-to-end smoke tests against qwen CLI using qwen_code_sdk.3 4This script is intentionally lightweight and avoids any test doubles.5It is useful for manual verification after changing SDK runtime behavior.6"""7 8from __future__ import annotations9 10import sys11 12if sys.version_info < (3, 10):  # noqa: UP03613    import json14 15    version = ".".join(str(part) for part in sys.version_info[:3])16    payload = {17        "ok": False,18        "stage": "startup",19        "error": f"Python >=3.10 is required, current version is {version}",20        "error_type": "RuntimeError",21    }22    print(json.dumps(payload, ensure_ascii=False, indent=2))23    raise SystemExit(2)24 25import argparse26import asyncio27import json28import subprocess29import threading30from collections.abc import AsyncIterator, Awaitable, Callable31from dataclasses import asdict, dataclass32from pathlib import Path33from queue import Empty, Queue34from typing import Any, TypeVar35 36SDK_ROOT = Path(__file__).resolve().parents[1]37SRC_ROOT = SDK_ROOT / "src"38if str(SRC_ROOT) not in sys.path:39    sys.path.insert(0, str(SRC_ROOT))40 41from qwen_code_sdk import (  # noqa: E40242    SDKUserMessage,43    SyncQuery,44    is_sdk_assistant_message,45    is_sdk_result_message,46    is_sdk_system_message,47    query,48    query_sync,49)50from qwen_code_sdk.transport import prepare_spawn_info  # noqa: E40251 52T = TypeVar("T")53 54 55@dataclass56class AsyncSingleResult:57    ok: bool58    assistant_text: str | None59    result_text: str | None60    session_id: str61 62 63@dataclass64class AsyncControlResult:65    ok: bool66    supported_commands_type: str67    saw_system_message: bool68    saw_result_message: bool69    session_id: str70 71 72@dataclass73class SyncResult:74    ok: bool75    saw_result_message: bool76    result_text: str | None77    session_id: str78 79 80def parse_args() -> argparse.Namespace:81    parser = argparse.ArgumentParser(82        description="Run real qwen_code_sdk smoke tests using qwen CLI",83    )84    parser.add_argument(85        "--qwen",86        default="qwen",87        help="Path or command for qwen executable (default: qwen)",88    )89    parser.add_argument(90        "--cwd",91        default=str(Path.cwd()),92        help="Working directory passed to SDK query options",93    )94    parser.add_argument(95        "--model",96        default=None,97        help="Optional model name. If set, script will call set_model(model).",98    )99    parser.add_argument(100        "--timeout-seconds",101        type=float,102        default=90.0,103        help="Timeout used for control/callback/stream-close options",104    )105    parser.add_argument(106        "--json-only",107        action="store_true",108        help="Print only JSON result (no progress logs)",109    )110    return parser.parse_args()111 112 113def check_qwen_cli_available(qwen_cmd: str, timeout_seconds: float) -> str:114    spawn_info = prepare_spawn_info(qwen_cmd)115    completed = subprocess.run(116        [spawn_info.command, *spawn_info.args, "--version"],117        check=True,118        capture_output=True,119        text=True,120        timeout=timeout_seconds,121    )122    return completed.stdout.strip()123 124 125def build_options(args: argparse.Namespace) -> dict[str, Any]:126    return {127        "cwd": args.cwd,128        "path_to_qwen_executable": args.qwen,129        "permission_mode": "yolo",130        "max_session_turns": 1,131        "timeout": {132            "control_request": args.timeout_seconds,133            "can_use_tool": args.timeout_seconds,134            "stream_close": args.timeout_seconds,135        },136    }137 138 139def extract_assistant_text(message: dict[str, Any]) -> str:140    content = message["message"].get("content", [])141    if not isinstance(content, list):142        return ""143 144    text_parts: list[str] = []145    for block in content:146        if isinstance(block, dict) and block.get("type") == "text":147            text_parts.append(str(block.get("text", "")))148    return "".join(text_parts)149 150 151async def run_async_single(args: argparse.Namespace) -> AsyncSingleResult:152    token = "SDK_REAL_ASYNC_OK"153    options = build_options(args)154    q = query(155        f"Reply exactly with {token}",156        options,157    )158 159    assistant_text: str | None = None160    result_text: str | None = None161    try:162        async for message in q:163            if is_sdk_assistant_message(message):164                assistant_text = (assistant_text or "") + extract_assistant_text(165                    message166                )167            if is_sdk_result_message(message):168                result_text = str(message.get("result", ""))169    finally:170        await q.close()171 172    ok = token in (assistant_text or "") and token in (result_text or "")173    return AsyncSingleResult(174        ok=ok,175        assistant_text=assistant_text,176        result_text=result_text,177        session_id=q.get_session_id(),178    )179 180 181async def run_async_controls(args: argparse.Namespace) -> AsyncControlResult:182    token = "SDK_REAL_CONTROL_OK"183    options = build_options(args)184    release_prompt = asyncio.Event()185 186    async def prompts() -> AsyncIterator[SDKUserMessage]:187        await release_prompt.wait()188        yield {189            "type": "user",190            "session_id": "00000000-0000-4000-8000-000000000001",191            "message": {192                "role": "user",193                "content": f"Reply exactly with {token}",194            },195            "parent_tool_use_id": None,196        }197 198    q = query(prompts(), options)199 200    supported: dict[str, Any] | None = None201    saw_system_message = False202    saw_result_message = False203    try:204        supported = await q.supported_commands()205        await q.set_permission_mode("plan")206        await q.set_permission_mode("yolo")207        if args.model:208            await q.set_model(args.model)209 210        release_prompt.set()211        async for message in q:212            if is_sdk_system_message(message):213                saw_system_message = True214            if is_sdk_result_message(message):215                saw_result_message = True216                break217    finally:218        await q.close()219 220    ok = isinstance(supported, dict) and saw_result_message221    return AsyncControlResult(222        ok=ok,223        supported_commands_type=type(supported).__name__,224        saw_system_message=saw_system_message,225        saw_result_message=saw_result_message,226        session_id=q.get_session_id(),227    )228 229 230async def run_stage(stage: str, coro: Awaitable[T], timeout_seconds: float) -> T:231    try:232        return await asyncio.wait_for(coro, timeout=timeout_seconds)233    except TimeoutError as exc:234        message = f"{stage} timed out after {timeout_seconds} seconds"235        raise TimeoutError(message) from exc236 237 238def run_sync(239    args: argparse.Namespace,240    on_query: Callable[[SyncQuery], None] | None = None,241) -> SyncResult:242    token = "SDK_REAL_SYNC_OK"243    options = build_options(args)244    q = query_sync(245        f"Reply exactly with {token}",246        options,247    )248    if on_query is not None:249        on_query(q)250 251    saw_result_message = False252    result_text: str | None = None253    try:254        for message in q:255            if is_sdk_result_message(message):256                saw_result_message = True257                result_text = str(message.get("result", ""))258                break259    finally:260        q.close()261 262    ok = saw_result_message and token in (result_text or "")263    return SyncResult(264        ok=ok,265        saw_result_message=saw_result_message,266        result_text=result_text,267        session_id=q.get_session_id(),268    )269 270 271def run_sync_with_timeout(args: argparse.Namespace) -> SyncResult:272    result_queue: Queue[SyncResult | BaseException] = Queue(maxsize=1)273    query_holder: dict[str, SyncQuery] = {}274 275    def remember_query(q: SyncQuery) -> None:276        query_holder["query"] = q277 278    def worker() -> None:279        try:280            result_queue.put(run_sync(args, on_query=remember_query))281        except BaseException as exc:282            result_queue.put(exc)283 284    thread = threading.Thread(285        target=worker,286        name="qwen-sdk-real-smoke-sync",287        daemon=True,288    )289    thread.start()290 291    try:292        item = result_queue.get(timeout=args.timeout_seconds)293    except Empty as exc:294        q = query_holder.get("query")295        if q is not None:296            q.close()297        raise TimeoutError(298            f"sync check timed out after {args.timeout_seconds} seconds"299        ) from exc300 301    thread.join(timeout=1.0)302    if isinstance(item, BaseException):303        raise item304    return item305 306 307def build_failure_payload(308    *,309    stage: str,310    exc: BaseException,311    qwen_version: str | None = None,312    completed: dict[str, Any] | None = None,313) -> dict[str, Any]:314    payload: dict[str, Any] = {315        "ok": False,316        "stage": stage,317        "error": str(exc),318        "error_type": type(exc).__name__,319    }320    if qwen_version is not None:321        payload["qwen_version"] = qwen_version322    if completed:323        payload["completed"] = completed324    return payload325 326 327async def main() -> int:328    args = parse_args()329 330    try:331        qwen_version = check_qwen_cli_available(args.qwen, args.timeout_seconds)332    except (subprocess.CalledProcessError, OSError, subprocess.TimeoutExpired) as exc:333        payload = build_failure_payload(stage="preflight", exc=exc)334        print(json.dumps(payload, ensure_ascii=False, indent=2))335        return 2336 337    stage = "async single-turn check"338    completed: dict[str, Any] = {}339    try:340        if not args.json_only:341            print(f"[smoke] qwen version: {qwen_version}")342            print(f"[smoke] running {stage}...")343        async_single = await run_stage(344            stage,345            run_async_single(args),346            args.timeout_seconds,347        )348        completed["async_single"] = asdict(async_single)349 350        stage = "async control check"351        if not args.json_only:352            print(f"[smoke] running {stage}...")353        async_controls = await run_stage(354            stage,355            run_async_controls(args),356            args.timeout_seconds,357        )358        completed["async_controls"] = asdict(async_controls)359 360        stage = "sync check"361        if not args.json_only:362            print(f"[smoke] running {stage}...")363        sync_result = run_sync_with_timeout(args)364        completed["sync"] = asdict(sync_result)365    except Exception as exc:366        payload = build_failure_payload(367            stage=stage,368            exc=exc,369            qwen_version=qwen_version,370            completed=completed,371        )372        print(json.dumps(payload, ensure_ascii=False, indent=2))373        return 1374 375    all_ok = async_single.ok and async_controls.ok and sync_result.ok376    payload = {377        "ok": all_ok,378        "qwen_version": qwen_version,379        "async_single": asdict(async_single),380        "async_controls": asdict(async_controls),381        "sync": asdict(sync_result),382    }383    print(json.dumps(payload, ensure_ascii=False, indent=2))384    return 0 if all_ok else 1385 386 387if __name__ == "__main__":388    raise SystemExit(asyncio.run(main()))389 
basant307/AI_Governance_Project · CoolFace