Created
March 29, 2026 03:15
-
-
Save ZavierChambers/593b89bde72efbb1e43ccad3be9ec921 to your computer and use it in GitHub Desktop.
Simple, reusable LM Studio client (OpenAI-compatible local API)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // lmstudio.js | |
| // Simple, reusable LM Studio client (OpenAI-compatible local API) | |
| const LMSTUDIO_BASE_URL = "http://localhost:1234/v1"; | |
| /** | |
| * Non-streaming completion | |
| */ | |
| export async function lmChat({ | |
| messages, | |
| temperature = 0.7, | |
| model = "local-model" | |
| }) { | |
| const res = await fetch(`${LMSTUDIO_BASE_URL}/chat/completions`, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json" | |
| }, | |
| body: JSON.stringify({ | |
| model, | |
| messages, | |
| temperature, | |
| stream: false | |
| }) | |
| }); | |
| if (!res.ok) { | |
| throw new Error(`LM Studio error: ${res.status}`); | |
| } | |
| const data = await res.json(); | |
| return data.choices?.[0]?.message?.content || ""; | |
| } | |
| /** | |
| * Streaming completion (token-by-token) | |
| */ | |
| export async function lmStream({ | |
| messages, | |
| onToken, | |
| onDone, | |
| onError, | |
| model = "local-model" | |
| }) { | |
| try { | |
| const res = await fetch(`${LMSTUDIO_BASE_URL}/chat/completions`, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json" | |
| }, | |
| body: JSON.stringify({ | |
| model, | |
| messages, | |
| stream: true | |
| }) | |
| }); | |
| if (!res.body) throw new Error("No response body"); | |
| const reader = res.body.getReader(); | |
| const decoder = new TextDecoder("utf-8"); | |
| let buffer = ""; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const lines = buffer.split("\n"); | |
| buffer = lines.pop(); // keep incomplete line | |
| for (const line of lines) { | |
| if (!line.startsWith("data: ")) continue; | |
| const json = line.replace("data: ", "").trim(); | |
| if (json === "[DONE]") { | |
| onDone && onDone(); | |
| return; | |
| } | |
| try { | |
| const parsed = JSON.parse(json); | |
| const token = parsed.choices?.[0]?.delta?.content; | |
| if (token) { | |
| onToken && onToken(token); | |
| } | |
| } catch (err) { | |
| // ignore partial JSON | |
| } | |
| } | |
| } | |
| onDone && onDone(); | |
| } catch (err) { | |
| onError && onError(err); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment