lmstudio_api_sample.py ダウンロード/コピー
lmstudio_api_sample.py をダウンロード
lmstudio_api_sample.py
lmstudio_api_sample.py
1#!/usr/bin/env python3
2"""
3概要:
4 LM Studio API sample (Python standard library only).
5詳細説明:
6 LM Studioのモデル一覧取得とチャットAPIのサンプルスクリプトです。
7 python lmstudio_api_sample.py --list のように実行してモデル一覧を取得したり、
8 python lmstudio_api_sample.py --model モデルID --prompt 質問 のようにチャットを行います。
9関連リンク:
10 lmstudio_api_sample_usage
11"""
12import argparse
13import json
14import sys
15from urllib.error import HTTPError, URLError
16from urllib.request import Request, ProxyHandler, build_opener
17
18
19def request_json(base_url, endpoint, timeout, payload=None):
20 """
21 概要:
22 JSON APIにHTTPリクエストを送信し、応答をパースして返します。
23 詳細説明:
24 システムプロキシをバイパスして直接サーバーに接続します。
25 payloadが指定された場合はJSONとしてエンコードし、POSTリクエストを送信します。
26 引数:
27 :param base_url: APIのベースURL
28 :type base_url: str
29 :param endpoint: リクエスト先のエンドポイント
30 :type endpoint: str
31 :param timeout: 通信タイムアウト秒
32 :type timeout: float
33 :param payload: リクエストボディとして送信するデータオブジェクト
34 :type payload: dict or None
35 戻り値:
36 :returns: 応答のJSON文字列をパースしたオブジェクト
37 :rtype: dict or list
38 """
39 data = None if payload is None else json.dumps(payload, ensure_ascii=False).encode("utf-8")
40 request = Request(
41 base_url.rstrip("/") + endpoint,
42 data=data,
43 headers={"Content-Type": "application/json", "Accept": "application/json"},
44 )
45 # Connect directly to the private server, bypassing system HTTP proxies.
46 with build_opener(ProxyHandler({})).open(request, timeout=timeout) as response:
47 return json.load(response)
48
49
50def main():
51 """
52 概要:
53 コマンドライン引数を解析し、APIへのリクエストを実行します。
54 詳細説明:
55 引数に応じてモデル一覧の表示、またはチャットAPIへの問い合わせを行います。
56 ネットワークエラーやパースエラーが発生した場合はエラーメッセージを標準エラー出力に書き出します。
57 戻り値:
58 :returns: プロセスの終了コード
59 :rtype: int
60 """
61 parser = argparse.ArgumentParser(description="LM Studioのモデル一覧取得とチャットAPIのサンプル")
62 parser.add_argument("--list", action="store_true", help="利用可能なモデルIDを表示(embeddingモデルも含む)")
63 parser.add_argument("--model", help="問い合わせるモデルID")
64 parser.add_argument("--prompt", help="モデルへの質問")
65 parser.add_argument("--base-url", default="http://192.168.27.18:1234/v1", help="APIのベースURL")
66 parser.add_argument("--timeout", type=float, default=300, help="通信タイムアウト秒(既定: 300)")
67 parser.add_argument("--max-tokens", type=int, default=2048, help="最大生成トークン数(推論トークンを含む)")
68 parser.add_argument("--show-reasoning", action="store_true", help="reasoning_contentも表示")
69 args = parser.parse_args()
70 if (args.model is None) != (args.prompt is None):
71 parser.error("--modelと--promptは両方指定してください")
72 if not args.list and args.model is None:
73 parser.error("--list、または--modelと--promptを指定してください")
74 if args.timeout <= 0 or args.max_tokens <= 0:
75 parser.error("--timeoutと--max-tokensは正の値を指定してください")
76 try:
77 if args.list:
78 result = request_json(args.base_url, "/models", args.timeout)
79 models = result["data"]
80 if not models:
81 print("利用可能なモデルはありません。")
82 for model in models:
83 print(model["id"])
84 if args.model is not None:
85 result = request_json(args.base_url, "/chat/completions", args.timeout, {
86 "model": args.model,
87 "messages": [{"role": "user", "content": args.prompt}],
88 "max_tokens": args.max_tokens,
89 "stream": False,
90 })
91 choice = result["choices"][0]
92 message = choice["message"]
93 if args.show_reasoning and message.get("reasoning_content"):
94 print("[reasoning]\n" + message["reasoning_content"] + "\n[answer]")
95 print(message.get("content") or "")
96 if choice.get("finish_reason") == "length":
97 print("生成上限に達しました。必要なら--max-tokensを増やしてください。", file=sys.stderr)
98 return 0
99 except HTTPError as exc:
100 print(f"HTTPエラー {exc.code}: {exc.read().decode('utf-8', errors='replace')}", file=sys.stderr)
101 except (URLError, TimeoutError, OSError) as exc:
102 print(f"接続エラー: {exc}", file=sys.stderr)
103 except (ValueError, KeyError, IndexError, TypeError) as exc:
104 print(f"API応答を解釈できません: {exc}", file=sys.stderr)
105 return 1
106
107
108if __name__ == "__main__":
109 # Preserve Japanese output in Windows terminals and redirected output.
110 for stream in (sys.stdout, sys.stderr):
111 if hasattr(stream, "reconfigure"):
112 stream.reconfigure(encoding="utf-8")
113 sys.exit(main())