채팅 앱 만들기
OpenAI 공식 SDK로 PleumRouter에 첫 메시지를 보내고, 스트리밍과 멀티턴 대화, 모델 교체, 비용 확인까지 만듭니다.
PleumRouter는 OpenAI 호환 API라서 base_url만 바꾸면 공식 SDK를 그대로 씁니다. 이 튜토리얼은 터미널에서 대화하는 작은 채팅 앱을 단계별로 만듭니다. 코딩 에이전트를 연결하려면 Claude Code나 Codex CLI로 가세요.
만들어 보기#
사전 준비
TypeScript는 Node.js 20 이상, Python은 3.10 이상이 필요합니다. 대시보드 → API 키에서
plm_로 시작하는 키를 만들고 환경변수로 넣어 두세요.Terminalexport PLEUM_API_KEY="plm_xxxxxxxxxxxxxxxx"프로젝트를 만들고 SDK 설치
Terminalmkdir pleum-chat && cd pleum-chat npm init -y npm pkg set type=module npm i openai npm i -D tsx typescript @types/nodemkdir pleum-chat && cd pleum-chat python3 -m venv .venv && source .venv/bin/activate pip install openai첫 메시지 보내기
base_url을 PleumRouter로 바꾼 클라이언트로 한 번 호출하고, 응답과 토큰 사용량을 출력합니다.chat.tsimport OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://apirouter.pleum.ai/v1", apiKey: process.env.PLEUM_API_KEY, }); const model = "gpt-4.1"; const res = await client.chat.completions.create({ model, messages: [{ role: "user", content: "Say hello in one sentence." }], }); console.log(res.choices[0].message.content); console.log(res.usage); // run: npx tsx chat.tsimport os from openai import OpenAI client = OpenAI( base_url="https://apirouter.pleum.ai/v1", api_key=os.environ["PLEUM_API_KEY"], ) model = "gpt-4.1" res = client.chat.completions.create( model=model, messages=[{"role": "user", "content": "Say hello in one sentence."}], ) print(res.choices[0].message.content) print(res.usage) # run: python chat.py응답을 스트리밍하기
stream: true를 주면 토큰이 생성되는 대로 도착합니다. 응답을 기다리는 시간이 훨씬 짧게 느껴집니다.chat.tsimport OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://apirouter.pleum.ai/v1", apiKey: process.env.PLEUM_API_KEY, }); const model = "gpt-4.1"; const stream = await client.chat.completions.create({ model, messages: [{ role: "user", content: "Write a haiku about routers." }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); } process.stdout.write("\n");import os from openai import OpenAI client = OpenAI( base_url="https://apirouter.pleum.ai/v1", api_key=os.environ["PLEUM_API_KEY"], ) model = "gpt-4.1" stream = client.chat.completions.create( model=model, messages=[{"role": "user", "content": "Write a haiku about routers."}], stream=True, ) for chunk in stream: if chunk.choices: print(chunk.choices[0].delta.content or "", end="", flush=True) print()멀티턴 대화 만들기
대화 기록(
messages)을 직접 쌓아 매번 함께 보내면 모델이 앞선 말을 기억합니다.exit를 입력하면 끝납니다.chat.tsimport * as readline from "node:readline/promises"; import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://apirouter.pleum.ai/v1", apiKey: process.env.PLEUM_API_KEY, }); const model = "gpt-4.1"; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: "You are a concise assistant." }, ]; while (true) { const text = (await rl.question("you> ")).trim(); if (!text || text === "exit") break; messages.push({ role: "user", content: text }); const stream = await client.chat.completions.create({ model, messages, stream: true }); let reply = ""; process.stdout.write("bot> "); for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content ?? ""; reply += delta; process.stdout.write(delta); } process.stdout.write("\n"); messages.push({ role: "assistant", content: reply }); } rl.close();import os from openai import OpenAI client = OpenAI( base_url="https://apirouter.pleum.ai/v1", api_key=os.environ["PLEUM_API_KEY"], ) model = "gpt-4.1" messages = [{"role": "system", "content": "You are a concise assistant."}] while True: text = input("you> ").strip() if not text or text == "exit": break messages.append({"role": "user", "content": text}) stream = client.chat.completions.create(model=model, messages=messages, stream=True) reply = "" print("bot> ", end="") for chunk in stream: if chunk.choices: delta = chunk.choices[0].delta.content or "" reply += delta print(delta, end="", flush=True) print() messages.append({"role": "assistant", "content": reply})모델 바꾸기
같은 코드로 모델만 바꿔 여러 회사의 모델을 씁니다. 정확한 ID는 모델 카탈로그나
GET /v1/models에서 확인하세요.Terminal# every model in the catalog curl https://apirouter.pleum.ai/v1/models # then change only the model id in chat.ts / chat.py, for example: # claude-sonnet-5 # gemini-3.8-flash # deepseek-ai/DeepSeek-V4.1-Flash비용 확인하기
비용은 응답 뒤에 정산됩니다. 응답 헤더의
X-Request-Id로GET /v1/generation?id=…를 조회하면cost_krw·cost_usd와 토큰 수를 받습니다. 스트리밍도 같은 방법이며, 대시보드 사용량에서도 볼 수 있습니다.chat.tsconst raw = await client.chat.completions .create({ model, messages: [{ role: "user", content: "Say hello in one sentence." }], }) .asResponse(); // the settled cost is looked up by request id const requestId = raw.headers.get("x-request-id"); const gen = await fetch(`https://apirouter.pleum.ai/v1/generation?id=${requestId}`, { headers: { Authorization: `Bearer ${process.env.PLEUM_API_KEY}` }, }); console.log(await gen.json()); // cost_krw, cost_usd, input_tokens, output_tokens ...import json import urllib.request raw = client.chat.completions.with_raw_response.create( model=model, messages=[{"role": "user", "content": "Say hello in one sentence."}], ) # the settled cost is looked up by request id request_id = raw.headers.get("x-request-id") req = urllib.request.Request( f"https://apirouter.pleum.ai/v1/generation?id={request_id}", headers={"Authorization": f"Bearer {os.environ['PLEUM_API_KEY']}"}, ) print(json.load(urllib.request.urlopen(req))) # cost_krw, cost_usd, input_tokens ...
확인하기#
- 첫 메시지에 응답이 오고
usage에 토큰 수가 찍힌다. - 스트리밍에서 글자가 조금씩 나타난다.
- 멀티턴에서 앞서 말한 내용을 기억한다.
model만 바꿔도 다른 모델이 응답한다.cost_krw값이 보인다.
다음 단계#
- Chat Completions — 요청 파라미터와 응답 필드 전체
- 스트리밍 — 경로별 SSE 형식과 비용 조회
- 모델·라우팅 고르기 — 용도에 맞는 모델 선택
- 코딩 에이전트 연결 — Claude Code·Codex 등 도구에 붙이기