API-テスト 「LM Studioのテスト」関係の「LM Studio」のAPI関係の記事を記述しています。 APIテスト APIの利用設定 APIを使用するために「LM Studio」のサーバー機能を次の様に有効化します。 画面右上の4つのアイコンから「Developer」を選択して設定を開始します。 一番上の「Local Server」を選択します。 「 + Load Model」ボタンを押して使用するモデルをロードします。 「Status Stoppend」スイッチを「ON」としてサーバーを起動します。 Developer Logsにログが出て来るので動作を確認してください。 テスト用のiOSアプリの制作 LLMサーバーと通信する簡単なチャットアプリを制作します。 アプリは、単純にメッセージを送信して結果を受け取るというシンプルな物です。 以下は今回制作したテストアプリのコードです。 iOS 26.0でビルドしてにiOS 26.4のシミュレータで動作確認しています。 全ソースコード // // ContentView.swift // ML ClientTest // // Created by Masahiko Tani on 2026/04/05. // import SwiftUI import Foundation struct ContentView: View { @State private var message = "" // 入力テキストを保持するステート変数 @State private var chatMessages: [ContentView.ChatMessage] = [] var body: some View { VStack { // テキストフィールド (入力欄) TextField("Enter message...", text: $message) .padding() // 送信ボタン Button("送信") { sendMessage() } .padding() // チャットメッセージの表示 ScrollView { // 縦にスクロールできるようにScrollViewを追加 LazyVStack { // 効率的な表示のためにLazyVStackを使用 ForEach(chatMessages) { message in // let markdown = message.text // Text(try! AttributedString(markdown: markdown)) Text(.init(message.text)) // markdown } } } } } func sendMessage() { guard !message.isEmpty else { return } // 空のメッセージは送信しない let url = URL(string: "http://127.0.0.1:1234/api/v1/chat")! // LM StudioのAPIエンドポイント var request = URLRequest(url: url) request.httpMethod = "POST" // POSTメソッドを使用 request.setValue("application/json", forHTTPHeaderField: "Content-Type") let chat = ChatRequest(input: message) do { request.httpBody = try JSONEncoder().encode(chat) } catch { // エンコード失敗時はここで処理 print("JSONエンコードに失敗しました: \(error)") } let task = URLSession.shared.dataTask(with: request) { (data, response, error) in do { handleServerResponse(data: data) // let json = try JSONDecoder().decode(String.self, from: data!) // サーバーからのJSON文字列をデコード // DispatchQueue.main.async { // let message = ContentView.ChatMessage(text: "You: \(json)", isUser: true) // self.chatMessages.append(message) // } } catch { DispatchQueue.main.async { let errorMsg = ContentView.ChatMessage( text: "Error: \(error.localizedDescription)", isUser: false // エラーは「Bot」側として扱う ) self.chatMessages.append(errorMsg) debugPrint(errorMsg.text) } } } task.resume() // 入力テキストをクリア message = "" } // data はサーバーから受け取った `Data`(URLSession などで取得) func handleServerResponse(data: Data?) { guard let data = data else { return } do { // ① JSON を `ChatResponse` にデコード let chatResp = try JSONDecoder().decode(ChatResponse.self, from: data) // ② type が "message" の要素を取得 if let messageOutput = chatResp.output.first(where: { $0.type == "message" }) { let serverMessage = messageOutput.content // ③ UI スレッドへ渡す DispatchQueue.main.async { let message = ContentView.ChatMessage(text: "You: \(serverMessage)", isUser: true) self.chatMessages.append(message) } } else { // "message" が見つからない場合のフォールバック let errorMsg = ContentView.ChatMessage( text: "⚠️ 返答に message がありませんでした", isUser: false // エラーは「Bot」側として扱う ) self.chatMessages.append(errorMsg) debugPrint(errorMsg.text) } } catch { DispatchQueue.main.async { let errorMsg = ContentView.ChatMessage( text: "Error: \(error.localizedDescription)", isUser: false // エラーは「Bot」側として扱う ) self.chatMessages.append(errorMsg) debugPrint(errorMsg.text) } } } struct ChatMessage: Identifiable { let id = UUID() let text: String let isUser: Bool // true なら「You」、false なら「Bot」 } // 2. JSON オブジェクトに包む struct ChatRequest: Codable { let input: String // let temperature: number // 任意(デフォルト 1.0) -生成のランダム性を調整 (0〜2) // let max_tokens: integer // 任意(デフォルト 512) -生成テキストの最大トークン数 var model: String = "openai/gpt-oss-20b" } /// 最外層 struct ChatResponse: Codable { let model_instance_id: String let output: [ChatOutput] let stats: ChatStats let response_id: String } /// output 配列の要素 struct ChatOutput: Codable { let type: String // "reasoning" / "message" let content: String } /// stats の情報(必要なら取り込めます) struct ChatStats: Codable { let input_tokens: Int let total_output_tokens: Int let reasoning_output_tokens: Int let tokens_per_second: Double let time_to_first_token_seconds: Double } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }   コードの簡単な説明 bodyでは大まかなUIを定義しています。 テキストフィールド (入力欄)/送信ボタン/チャットメッセージの表示などです。 var body: some View 送信ボタンが押されるとsendMessageが呼ばれ、入力されたメッセージをJSONエンコードしてhttpのPOSTで送ります。送信後はfunc handleServerResponse(data: Data?)で受信します。 func sendMessage() 「LM Studio」のサーバーログの例です。 クライアントからメッセージを受け取り推論してレスポンス生成し返しています。 2026-04-09 02:47:37 [DEBUG] Received request: POST to /api/v1/chat with body { "model": "openai/gpt-oss-20b", "input": "こんにちは" } 2026-04-09 02:47:37 [INFO] [openai/gpt-oss-20b] Running api/v1/chat on history with 1 message. 2026-04-09 02:47:38 [INFO] [openai/gpt-oss-20b] Prompt processing progress: 100.0% 2026-04-09 02:47:38 [INFO] [openai/gpt-oss-20b] Prompt processing progress: 100.0% 2026-04-09 02:47:38 [INFO] [openai/gpt-oss-20b] Generated response: { "model_instance_id": "openai/gpt-oss-20b", "output": [ { "type": "reasoning", "content": "Need greet in Japanese." }, { "type": "message", "content": "こんにちは!今日はどんなことに興味がありますか?お手伝いできることがあれば教えてくださいね。" } ], "stats": { "input_tokens": 68, "total_output_tokens": 42, "reasoning_output_tokens": 5, "tokens_per_second": 75.68517600625522, "time_to_first_token_seconds": 1.129 }, "response_id": "resp_850cca25c267606a71f269820dafb2bd6a99b2e5a6d8f81f" } コードの説明を追加 制作したアプリのプログラムに関して追加説明をします。 まず、JSONとかチャットの為にstructを複数定義しています。 ChatMessage : やり取りのメッセージ格納用 ChatRequest :LLMサーバーのAPIに対してJSONを組み立てるメッセージ格納用 ChatResponse :サーバーからのレスポンスの格納用JSONフォーマット ChatOutput :ChatResponseの出力用JSONフォーマット ChatStats : ChatResponseのstats の情報用のJSONフォーマット ChatMessageは、送信/受信/表示などに使用されています。 ChatRequestは「/api/v1」用のJSONを組み立てています。現在は、input/modelが必須のバラメータの様です ChatResponseは、サーバーからのレスポンスでChatOutputとChatStatsを含みます。推論したモデル名/ChatOutput/ChatStatsを含みます。 ChatOutputは、reasoning/messageの2つの内容を含みます。messageは実際の推論結果となります。 ChatStatsは、トークンの利用状態とか処理時間などです。