# OpenAI SDK > Python, TypeScript, Go 공식 OpenAI 클라이언트로 Kiln을 호출하는 방법입니다. 설치, 검증한 요청 예제, 툴 호출 왕복, 그리고 OpenAI 스키마에 없는 cost 필드와 max_tokens 안의 reasoning 예산 등 다른 점을 정리합니다. Source: https://kiln.bricksum.com/docs/ko/sdks/openai Kiln은 OpenAI Chat Completions API를 제공하므로 공식 OpenAI 클라이언트가 중간 어댑터 없이 그대로 연결됩니다. 바꾸는 값은 base URL, 키, 모델 ID 세 가지입니다. ## 설치 **Python** ```bash pip install openai ``` **TypeScript** ```bash npm install openai ``` **Go** ```bash go get github.com/openai/openai-go/v3 ``` ## 연결 **Python** ```python import os from openai import OpenAI client = OpenAI( base_url="https://api.bricksum.com/v1", api_key=os.environ["KILN_API_KEY"], # sk-bk-... ) resp = client.chat.completions.create( model="qwen3-32b", messages=[{"role": "user", "content": "Write a haiku about kilns."}], max_tokens=800, ) print(resp.choices[0].message.content) ``` **TypeScript** ```javascript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.bricksum.com/v1", apiKey: process.env.KILN_API_KEY, // sk-bk-... }); const resp = await client.chat.completions.create({ model: "qwen3-32b", messages: [{ role: "user", content: "Write a haiku about kilns." }], max_tokens: 800, }); console.log(resp.choices[0].message.content); ``` **Go** ```go package main import ( "context" "fmt" "os" "github.com/openai/openai-go/v3" "github.com/openai/openai-go/v3/option" ) func main() { client := openai.NewClient( option.WithBaseURL("https://api.bricksum.com/v1"), option.WithAPIKey(os.Getenv("KILN_API_KEY")), ) resp, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{ Model: "qwen3-32b", MaxTokens: openai.Int(800), Messages: []openai.ChatCompletionMessageParamUnion{ openai.UserMessage("Write a haiku about kilns."), }, }) if err != nil { panic(err) } fmt.Println(resp.Choices[0].Message.Content) } ``` ## 툴 호출 tool_choice를 "auto"로 둔 왕복 한 번입니다. 모델이 툴을 요청하면 코드가 실행하고, 그 결과를 tool 메시지로 돌려보냅니다. 모든 툴에는 description이 있어야 하며, 없는 툴은 모델 서버가 400으로 거부합니다. **Python** ```python import json, os from openai import OpenAI client = OpenAI(base_url="https://api.bricksum.com/v1", api_key=os.environ["KILN_API_KEY"]) tools = [{"type": "function", "function": { "name": "get_weather", "description": "Current weather for a city", # required "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}, }}] messages = [{"role": "user", "content": "What's the weather in Seoul?"}] first = client.chat.completions.create(model="qwen3-32b", messages=messages, tools=tools, tool_choice="auto", max_tokens=800) reply = first.choices[0].message # The model may answer without a tool call, and one reply can carry several. if reply.tool_calls: messages.append(reply) for call in reply.tool_calls: messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps({"temp_c": 27})}) final = client.chat.completions.create(model="qwen3-32b", messages=messages, max_tokens=800) print(final.choices[0].message.content) else: print(reply.content) ``` **TypeScript** ```javascript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.bricksum.com/v1", apiKey: process.env.KILN_API_KEY }); const tools: OpenAI.ChatCompletionTool[] = [{ type: "function", function: { name: "get_weather", description: "Current weather for a city", // required parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] }, }}]; const messages: OpenAI.ChatCompletionMessageParam[] = [ { role: "user", content: "What's the weather in Seoul?" }, ]; const first = await client.chat.completions.create({ model: "qwen3-32b", messages, tools, tool_choice: "auto", max_tokens: 800 }); // The model may answer without a tool call, and one reply can carry several. const calls = first.choices[0].message.tool_calls ?? []; if (calls.length > 0) { messages.push(first.choices[0].message); for (const call of calls) { if (call.type !== "function") continue; messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ temp_c: 27 }) }); } const final = await client.chat.completions.create({ model: "qwen3-32b", messages, max_tokens: 800 }); console.log(final.choices[0].message.content); } else { console.log(first.choices[0].message.content); } ``` ## Kiln에서 다른 점 - **usage.cost는 OpenAI 스키마에 없음** Kiln은 모든 응답의 usage 객체에 USD 단위 cost를 더해 보냅니다. OpenAI 클라이언트는 usage를 OpenAI 스키마대로 타입 지정하고 그 스키마에는 이 필드가 없으므로, 타입을 넓히기 전까지 TypeScript가 resp.usage.cost를 거부합니다. 값 자체는 어느 쪽이든 응답에 담겨 옵니다. - **reasoning 토큰도 max_tokens에 포함** 모델은 답하기 전에 reasoning을 수행하며 그 토큰도 과금되고 max_tokens 안에 포함됩니다. 예산이 작으면 message가 빈 채로 finish_reason이 "length"로 돌아오므로 둘 다 들어갈 여유를 두세요. 예제의 800은 이 모델에서 측정하지 않은 시작값이므로, content가 비고 finish_reason이 "length"이면 늘리세요. base URL, 인증, 모든 엔드포인트가 공유하는 요청 파라미터, 응답 형식, 스트리밍, 에러 코드는 어떤 SDK를 쓰든 같습니다. 이 내용은 [API 레퍼런스](https://kiln.bricksum.com/docs/ko/api-reference)에 한 번만 정리돼 있습니다. 위 호출이 도달하는 엔드포인트는 [Chat completions](https://kiln.bricksum.com/docs/ko/api-reference/chat-completions)입니다. 모델 ID는 API가 지금 서빙 중인 것이어야 합니다. 목록을 조회하는 방법과 원하는 ID가 목록에 없을 때 할 일은 [모델](https://kiln.bricksum.com/docs/ko/models) 페이지에 있습니다. SDK와 무관하게 이 모델이 지원하지 않는 기능은 [OpenAI에서 옮겨오기](https://kiln.bricksum.com/docs/ko/migrating-from-openai) 페이지에 정리돼 있습니다. 참고 이 페이지의 스니펫은 2026-09-21에 openai 3.16.2 (Python), openai 7.20.0 (Node), openai-go v3.64.0 환경에서 Kiln API로 직접 실행해 확인했습니다. 확인하지 않은 부분: 그 실행은 gpt-oss-120b에서 했습니다. 페이지는 이제 qwen3-32b을 사용하며, 이 모델로는 아직 다시 실행하지 않았습니다. 툴 호출 탭에서 모든 툴 호출에 답하는 반복문은 그 실행 이후에 추가됐으며 API로 실행하지 않았습니다.