{ "nbformat": 4, "nbformat_minor": 5, "metadata": { "colab": { "name": "Arena Playwright API Colab Test", "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Arena Playwright API — Google Colab 測試\n", "\n", "先在 Colab 左側的鑰匙圖示(Secrets)新增 `BRIDGE_API_KEY`,值設定為 Hugging Face Space 的同名 Secret。不要直接把 API Key 寫進 Notebook。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip -q install openai requests" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from google.colab import userdata\n", "import json\n", "import requests\n", "\n", "# 改成你的直接 Space 網址,不是 huggingface.co/spaces/... 頁面。\n", "SPACE_URL = \"https://YOUR-SPACE.hf.space\".rstrip(\"/\")\n", "API_KEY = userdata.get(\"BRIDGE_API_KEY\")\n", "\n", "assert \"YOUR-SPACE\" not in SPACE_URL, \"請先修改 SPACE_URL\"\n", "assert API_KEY, \"請在 Colab Secrets 新增 BRIDGE_API_KEY\"\n", "\n", "HEADERS = {\"Authorization\": f\"Bearer {API_KEY}\"}\n", "print(\"設定已載入;API Key 不會顯示。\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Health Check" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = requests.get(f\"{SPACE_URL}/health\", timeout=60)\n", "print(\"HTTP\", response.status_code)\n", "print(json.dumps(response.json(), ensure_ascii=False, indent=2))\n", "response.raise_for_status()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "正常應顯示 `status: ok`、`configured: true`、`browser_ready: true`。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. 取得模型清單" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = requests.get(\n", " f\"{SPACE_URL}/v1/models\",\n", " headers=HEADERS,\n", " timeout=180,\n", ")\n", "print(\"HTTP\", response.status_code)\n", "if not response.ok:\n", " print(response.text)\n", "response.raise_for_status()\n", "\n", "model_ids = [item[\"id\"] for item in response.json().get(\"data\", [])]\n", "print(f\"找到 {len(model_ids)} 個模型\")\n", "print(\"前 20 個:\")\n", "for model_id in model_ids[:20]:\n", " print(\"-\", model_id)\n", "\n", "MODEL = \"Max\" if \"Max\" in model_ids else model_ids[0]\n", "print(\"測試模型:\", MODEL)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 非串流 Chat Completions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "payload = {\n", " \"model\": MODEL,\n", " \"messages\": [\n", " {\"role\": \"user\", \"content\": \"請只回答:Colab 測試成功\"}\n", " ],\n", " \"stream\": False,\n", "}\n", "\n", "response = requests.post(\n", " f\"{SPACE_URL}/v1/chat/completions\",\n", " headers={**HEADERS, \"Content-Type\": \"application/json\"},\n", " json=payload,\n", " timeout=300,\n", ")\n", "print(\"HTTP\", response.status_code)\n", "if not response.ok:\n", " print(response.text)\n", "response.raise_for_status()\n", "result = response.json()\n", "print(result[\"choices\"][0][\"message\"][\"content\"] )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. SSE 串流測試" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "payload[\"messages\"] = [{\"role\": \"user\", \"content\": \"用三個短句介紹台北。\"}]\n", "payload[\"stream\"] = True\n", "\n", "with requests.post(\n", " f\"{SPACE_URL}/v1/chat/completions\",\n", " headers={**HEADERS, \"Content-Type\": \"application/json\"},\n", " json=payload,\n", " stream=True,\n", " timeout=300,\n", ") as response:\n", " print(\"HTTP\", response.status_code)\n", " if not response.ok:\n", " print(response.text)\n", " response.raise_for_status()\n", " for raw_line in response.iter_lines(decode_unicode=True):\n", " if not raw_line or not raw_line.startswith(\"data: \" ) or raw_line == \"data: [DONE]\":\n", " continue\n", " event = json.loads(raw_line[6:])\n", " if \"error\" in event:\n", " print(\"\\nERROR:\", event[\"error\"])\n", " break\n", " delta = event.get(\"choices\", [{}])[0].get(\"delta\", {}).get(\"content\", \"\")\n", " print(delta, end=\"\", flush=True)\n", "print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. 使用 OpenAI Python SDK" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from openai import OpenAI\n", "\n", "client = OpenAI(\n", " base_url=f\"{SPACE_URL}/v1\",\n", " api_key=API_KEY,\n", " timeout=300,\n", ")\n", "\n", "completion = client.chat.completions.create(\n", " model=MODEL,\n", " messages=[{\"role\": \"user\", \"content\": \"請回答:OpenAI SDK 測試成功\"}],\n", ")\n", "print(completion.choices[0].message.content)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 常見結果\n", "\n", "- `401`:Colab 的 `BRIDGE_API_KEY` 與 Space Secret 不一致。\n", "- `503 arena_login_required`:Playwright 尚未登入成功,請到 `/admin` 重試。\n", "- `404 model_not_found`:模型清單已變更,重新執行 models cell。\n", "- `504 upstream_timeout`:Arena 回應過慢,可重試或改用較快模型。\n", "- Private Space 可能先被 Hugging Face 存取控制擋住;先在瀏覽器確認 Space 可存取。" ] } ] }