メインコンテンツへスキップ

APIテスト

APIの利用設定

APIを使用するために「LM Studio」のサーバー機能を次の様に有効化します。

画面右上の4つのアイコンから「Developer」を選択して設定を開始します。

スクリーンショット 2026-04-09 2.25.30.png

一番上の「Local Server」を選択します。

スクリーンショット 2026-04-09 2.26.15.png

「 + Load Model」ボタンを押して使用するモデルをロードします。

スクリーンショット 2026-04-09 2.29.20.png

「Status Stoppend」スイッチを「ON」としてサーバーを起動します。

スクリーンショット 2026-04-09 2.43.08.png

Developer Logsにログが出て来るので動作を確認してください。

スクリーンショット 2026-04-09 2.33.02.png

テスト用のiOSアプリの制作

LLMサーバーと通信する簡単なチャットアプリを制作します。

アプリは、単純にメッセージを送信して結果を受け取るというシンプルな物です。

スクリーンショット 2026-04-09 2.47.53.png

以下は今回制作したテストアプリのコードです。

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は、トークンの利用状態とか処理時間などです。