# KT 토큰팩토리 개발 문서 원문: https://tokenfactory.kt.com/docs # KT Token Factory AI Gateway > 하나의 API Key로 KT 토큰팩토리에서 제공하는 여러 프로바이더의 모델을 호출합니다. > OpenAI SDK와 100% 호환되므로 기존 코드에서 `base_url`만 바꾸면 됩니다. > 신규 개발에는 **Chat Completions**를 권장합니다. 고급 기능은 모델별 지원 범위를 확인하세요. ```text Base URL https://api.tokenfactory.kt.com/v1 API Path /chat/completions, /responses, /messages Auth Authorization: Bearer {API_KEY} ``` 예제의 모델 ID는 실제 모델명이 아닙니다. `GET /models`에서 확인한 ID로 바꾸세요. 이 문서의 API 경로는 Base URL 뒤에 붙입니다. # 시작하기 ## 5분 만에 시작하기 **Step 1 — API Key 준비** 서비스 관리자에게 발급받은 키를 서버 환경변수에 설정합니다. ```bash export TOKENFACTORY_BASE_URL="https://api.tokenfactory.kt.com/v1" export TOKENFACTORY_API_KEY="발급받은_API_KEY" export TOKENFACTORY_MODEL="모델_ID" ``` **Step 2 — 연결·모델 확인** ```bash curl "${TOKENFACTORY_BASE_URL}/models" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" ``` 응답 배열에서 모델 ID를 확인하고 예제의 `모델_ID`와 `TOKENFACTORY_MODEL`을 바꾸세요. **Step 3 — 첫 요청 보내기** ```bash curl "${TOKENFACTORY_BASE_URL}/chat/completions" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "모델_ID", "messages": [ {"role": "system", "content": "사내 위키 문서를 세 문장으로 요약하세요."}, {"role": "user", "content": "이번 분기부터 배포 프로세스가 매일 배포로 바뀌었습니다."} ] }' ``` 답변은 `choices[0].message.content`에서 읽습니다. ## 인증 | 항목 | 안내 | | --- | --- | | 사용할 키 | KT 토큰팩토리에서 발급받은 API Key | | 전달 방식 | `Authorization: Bearer {API_KEY}` | | 적용 범위 | 모델 목록 조회를 포함한 API 요청 | | Base URL 설정 | `/v1`까지 포함. 개별 API 경로와 `/v1`을 중복 추가하지 않기 | ### 키 사용 조건 | 항목 | 확인 사항 | | --- | --- | | 키 상태·기간 | 활성 상태이며 사용 가능한 기간인지 확인 | | 사용 모델 | 모델 목록과 서비스에서 안내받은 사용 조건 확인 | ### 키 상태 및 에러 동작 | 상황 | HTTP | code | 재시도·조치 | | --- | ---: | --- | :---: | | 키 누락·유효하지 않은 키 | 401 | `TF-E401-01` | ❌ Bearer 형식과 키 확인 | | 비활성·사용 기간 외 | 401 | `TF-E401-01` | ❌ 키 상태·기간 확인 후 교체 | | 일시적으로 인증 처리 불가 | 503 | `TF-E503-01` | 일시 장애이면 대기 후 제한적으로 재시도 | ### API Key 보관 - 서버 환경변수 또는 Secret Manager에 보관하세요. - 브라우저·모바일 앱·공개 저장소에 키를 직접 넣지 마세요. 클라이언트 앱은 서버를 통해 호출합니다. - 노출된 키는 폐기·재발급을 요청하고 배포 환경의 키를 교체하세요. - 문의 자료와 애플리케이션 로그에서 API Key를 제거하세요. ### 401 vs 403 구분 기준 | HTTP | 의미 | 조치 | | --- | --- | --- | | 401 | 인증 실패 | 키와 Bearer 헤더 확인 | | 403 | 정책에 의해 요청 차단 | 요청 내용·가드레일 등 서비스 사용 조건 확인 | # 모델과 라우팅 ## 모델 ID와 라우팅 Space는 서비스의 공통 설정이 적용되는 작업 공간입니다. ### 모델 선택 규칙 | 지정 방식·상황 | 안내 | | --- | --- | | 모델 ID | `GET /models` 응답의 `model` 필드에서 확인한 모델 ID를 요청에 사용 | | `auto` | Model Auto Router 활성화로 모델 자동 선택 | | 사용할 수 없는 모델 | `400 TF-E400-02` 가능 | ### 문맥 길이와 출력 예산 | 항목 | 사용 방법 | | --- | --- | | 입력 길이 | 시스템 지침·이전 대화·tool 정의와 결과를 포함해 확인 | | 출력 예산 | 모델이 지원하는 `max_tokens` 또는 `max_completion_tokens` 중 하나 사용 | | 추론 모델 | 출력 예산의 일부가 추론에 사용될 수 있음 | | 입력이 너무 김 | 이전 대화를 요약하거나 불필요한 메시지·tool 결과 제거 | | `finish_reason: length` | 출력이 잘렸을 수 있으므로 답변·JSON·tool 인자의 완성 여부 확인 | # API ## API 레퍼런스 ### `GET /models` 현재 서비스의 모델 목록을 조회합니다. 서비스에 설정된 모델 사용 범위가 목록에 반영됩니다. ```bash curl "${TOKENFACTORY_BASE_URL}/models" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" ``` **응답은 JSON 배열입니다.** 아래는 모델 항목의 예시이며, 세부 필드는 제공 모델에 따라 다릅니다. ```json [ { "id": "qwen/qwen3-32b", "object": "model", "created": "1745852400", "owned_by": "Alibaba" } ] ``` | 항목 | 읽는 방법 | | --- | --- | | `id` | 모델 목록 항목의 식별자이며, 요청의 `model` 값으로 사용 | | `object` | 항목의 타입. 모델의 경우 `model`. | | `owned_by` | 모델 개발사. 예: `Alibaba` | | `created` | 모델 등록일 | ### `POST /chat/completions` OpenAI 호환 Chat Completions 경로입니다. 모델이 지원하는 스트리밍·도구 호출·구조화 출력 등을 사용할 수 있습니다. ```bash curl "${TOKENFACTORY_BASE_URL}/chat/completions" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "모델_ID", "messages": [{"role": "user", "content": "고객 문의를 배송·환불·기타로 분류해 주세요."}], "max_tokens": 128, "metadata": {"team": "cs-bot", "env": "staging"} }' ``` | role | 용도 | | --- | --- | | `system` | 모델의 역할·답변 지침 | | `user` | 사용자 입력 | | `assistant` | 이전 모델 응답·도구 호출 요청 | | `tool` | 애플리케이션이 실행한 도구 결과 | 다중 턴 대화는 이전 메시지를 순서대로 `messages`에 포함합니다. 추가 역할과 고급 옵션은 모델별 지원 여부를 확인하세요. #### 스펙 자동 변환 Chat Completions 요청을 대상 모델의 규격으로 변환할 수 있습니다. 호출자는 Chat Completions 형식으로 요청하고 응답을 받습니다. | 방향 | 지원 범위 | | --- | --- | | Chat Completions → Messages / Responses | 지원 모델에서 자동 변환 | | Messages / Responses → 다른 규격 | 같은 방식의 자동 변환을 전제로 사용하지 않기 | | 변환할 수 없는 요청 | `422 TF-E422-01` 가능 | #### 응답 읽기 일반 텍스트 응답 예시입니다. ```json { "id": "chatcmpl-example", "object": "chat.completion", "created": 0, "model": "model-id", "choices": [ { "index": 0, "message": {"role": "assistant", "content": "배송 문의입니다."}, "finish_reason": "stop" } ], "usage": {"prompt_tokens": 20, "completion_tokens": 15, "total_tokens": 35} } ``` | 필드 | 의미 | | --- | --- | | `choices[].message.content` | 답변 텍스트. 도구 호출 시 비어 있거나 null일 수 있음 | | `choices[].message.tool_calls` | 앱이 실행할 도구와 인자 | | `choices[].message.reasoning` | 제공되는 경우 추론 텍스트·요약. 최종 답변과 별도 처리 | | `choices[].message.refusal` | 제공되는 경우 모델의 거절 안내 | | `usage` | 제공되는 경우 토큰 사용량 | | `finish_reason` | 의미·처리 | | --- | --- | | `stop` | 생성 종료. 거절 여부와 본문 확인 | | `length` | 길이 제한. 답변·도구 인자가 완성되었는지 확인 | | `tool_calls` | 도구 호출 요청. 앱에서 검증 후 처리 | | `content_filter` | 콘텐츠 제한으로 종료. 정상 답변과 구분 | #### SSE 스트리밍 응답 형식 `stream: true`이면 SSE로 결과를 받습니다. #### 다중 턴 대화 이전 사용자 메시지와 모델 답변을 순서대로 포함해 후속 질문을 보냅니다. ```json { "model": "모델_ID", "messages": [ {"role": "system", "content": "한국어로 짧게 답하세요."}, {"role": "user", "content": "AI Gateway의 장점을 알려주세요."}, {"role": "assistant", "content": "여러 AI 모델을 공통 API로 호출할 수 있습니다."}, {"role": "user", "content": "그 장점을 예시로 설명해 주세요."} ] } ``` 문맥 길이는 이전 대화까지 포함합니다. 길이가 늘어나면 입력과 출력 예산을 조정하세요. #### Reasoning 요청 `reasoning_effort`를 지원하는 모델의 요청 예시입니다. ```json { "model": "추론을_지원하는_모델_ID", "messages": [{"role": "user", "content": "세 가지 대안을 비용과 성능 관점에서 비교해 주세요."}], "reasoning_effort": "medium" } ``` | 항목 | 안내 | | --- | --- | | `low`·`medium`·`high` | 지원 모델에서 추론 수준 요청. 허용 값·효과는 모델별 확인 | | 추론 끄기·추가 수준 | `none` 등 다른 값의 지원 여부는 모델별 확인 | | 응답 시간·토큰 | 추론 수준에 따라 늘어날 수 있음 | #### JSON·구조화 출력 JSON Schema를 지원하는 모델의 요청 예시입니다. ```json { "model": "구조화_출력을_지원하는_모델_ID", "messages": [{"role": "user", "content": "고객 문의를 분류하세요: 배송이 늦어지고 있습니다."}], "response_format": { "type": "json_schema", "json_schema": { "name": "inquiry_category", "strict": true, "schema": { "type": "object", "properties": { "category": {"type": "string", "enum": ["배송", "환불", "기타"]} }, "required": ["category"], "additionalProperties": false } } } } ``` `choices[0].message.content`는 JSON 문자열입니다. 이를 파싱한 결과 예시: ```json {"category": "배송"} ``` | 방식 | 사용 방법 | | --- | --- | | JSON object | `response_format: {"type": "json_object"}`와 메시지의 JSON 출력 지침 사용 | | JSON Schema | 스키마와 `strict` 옵션을 지원하는 모델 사용 | | 결과 처리 | 오류·거절·길이 제한 확인 후 JSON 파싱·스키마 검증 | #### Function tool 호출 **① 도구 정의와 첫 요청** ```json { "model": "tool을_지원하는_모델_ID", "messages": [{"role": "user", "content": "서울 날씨를 알려주세요."}], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "도시의 현재 날씨를 조회합니다.", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } } ], "tool_choice": "auto" } ``` **② 도구 호출 응답** `finish_reason: tool_calls`인 응답의 `choices[0].message` 예시입니다. ```json { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_123", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"서울\"}"} } ] } ``` 모델은 함수와 인자를 제안합니다. 앱에서 JSON 문자열인 `arguments`를 파싱·검증하고 허용된 함수를 실행합니다. **③ 도구 실행 결과 전달** 원래 대화와 assistant 호출을 유지하고, 같은 `tool_call_id`로 결과를 추가합니다. ```json { "model": "tool을_지원하는_모델_ID", "messages": [ {"role": "user", "content": "서울 날씨를 알려주세요."}, { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_123", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"서울\"}"} } ] }, {"role": "tool", "tool_call_id": "call_123", "content": "맑음, 18°C"} ] } ``` | 상황 | 앱의 처리 | | --- | --- | | 여러 tool 호출 | 각 호출 ID에 맞는 결과 전달 | | 결제·발송·데이터 변경 | 업무 식별값과 실행 이력으로 중복 실행 방지 | | 스트리밍 tool 호출 | 인자를 모두 받은 뒤 처리 | ### `POST /responses` Responses 형식의 기존 클라이언트를 위한 경로입니다. 해당 형식을 지원하는 모델을 사용하세요. | Parameter | Type | 설명 | | --- | --- | --- | | `model` | string | **필수.** 지원 모델 ID | | `input` | string 또는 array | **필수.** 입력 텍스트·입력 항목 배열 | | `instructions` | string | 답변 지침 | | `max_output_tokens` | integer | 출력 예산. 추론 토큰이 포함될 수 있음 | | `tools` | array | 지원되는 도구 정의 | | `stream` | boolean | Responses 형식의 SSE | | `store` | boolean | `false` 명시 | ```bash curl "${TOKENFACTORY_BASE_URL}/responses" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "Responses를_지원하는_모델_ID", "instructions": "한 문장으로 답하세요.", "input": "사내 VPN 접속이 안 될 때 먼저 확인할 것은 무엇인가요?", "max_output_tokens": 200, "store": false }' ``` | 상태 기능 | 안내 | | --- | --- | | `store: true`·`previous_response_id` | 미지원. `422` 가능 | | 대화 문맥 | 이전 문맥을 매 요청의 입력에 직접 포함 | | 응답 ID | 저장 응답 조회·연결을 전제로 대화 관리하지 않기 | **일반 응답 예시** ```json { "id": "resp_example", "object": "response", "status": "completed", "model": "model-id", "output": [ { "id": "msg_example", "type": "message", "role": "assistant", "status": "completed", "content": [{"type": "output_text", "text": "네트워크 연결과 VPN 계정 상태를 확인하세요."}] } ], "usage": {"input_tokens": 20, "output_tokens": 15, "total_tokens": 35} } ``` **Response 구조 차이** | 항목 | Chat Completions | Responses | | --- | --- | --- | | 답변 위치 | `choices[].message.content` | `output`의 message → content의 `output_text` | | 토큰 필드 | `prompt_tokens` / `completion_tokens` | `input_tokens` / `output_tokens` | | 정상 완료 | `finish_reason` 확인 | `status: completed` 확인 | | 스트림 종료 | `[DONE]` | `response.completed` 등 terminal event·상태 확인 | 첫 `output` 항목이 텍스트 메시지라는 가정은 피하세요. `failed`·`incomplete` 상태는 정상 완료와 구분합니다. ### `POST /messages` Messages 형식의 기존 클라이언트를 위한 경로입니다. 해당 형식을 지원하는 모델을 사용하세요. | Parameter | Type | 설명 | | --- | --- | --- | | `model` | string | **필수.** 지원 모델 ID | | `messages` | array | **필수.** user·assistant 대화 | | `system` | string 또는 array | 시스템 지침 | | `max_tokens` | integer | 최대 출력 토큰 | | `stream` | boolean | Messages 형식의 SSE 요청 | | `tools` | array | Messages 형식의 도구 정의 | ```bash curl "${TOKENFACTORY_BASE_URL}/messages" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "Messages를_지원하는_모델_ID", "system": "한국어로 간결하게 답하세요.", "messages": [{"role": "user", "content": "서비스 점검 공지를 한 문장으로 작성해 주세요."}], "max_tokens": 200 }' ``` | 항목 | 안내 | | --- | --- | | 인증 | KT 토큰팩토리 Bearer 키 사용 | | 시스템 지침 | 본문의 `system`에 지정 | | 최대 출력 | `max_tokens`에 지정 | | 답변 | `content` 배열의 text 블록에서 읽기 | | 스트리밍 | `content_block_delta`, `message_stop` 등 Messages event 처리 | | 종료 사유 | `end_turn`·`tool_use`·`max_tokens` 등을 구분 | **일반 응답 예시** ```json { "id": "msg_example", "type": "message", "role": "assistant", "model": "model-id", "content": [{"type": "text", "text": "서비스 점검 시간 동안 접속이 일시 중단될 수 있습니다."}], "stop_reason": "end_turn", "usage": {"input_tokens": 20, "output_tokens": 15} } ``` 텍스트는 `content`의 text 블록에서 읽습니다. 기존 버전·기능 헤더와 도구 정의는 대상 모델의 지원 조건에 맞게 적용하세요. ### Legacy Completions `POST /completions`는 이 문서의 지원 API에 포함하지 않습니다. 기존 `prompt` 요청은 Chat Completions의 `messages` 형식으로 전환하세요. ## 요청 파라미터 사전 `POST /chat/completions` 기준입니다. 선택 항목의 기본값·허용 범위·지원 여부는 모델에 따라 다릅니다. > 미지원 파라미터가 항상 무시되는 것은 아닙니다. 적용되지 않거나 오류가 발생할 수 있으므로 필요한 기능의 결과를 확인하세요. **필수** | Parameter | Type | 설명 | | --- | --- | --- | | `model` | string | 모델 ID 또는 사용 조건을 충족하는 `auto` | | `messages` | array | 역할과 내용을 포함한 대화 메시지 목록 | **Sampling · 응답 옵션** | Parameter | Type | 설명 | | --- | --- | --- | | `temperature` | number | 생성의 다양성 조절 | | `top_p` | number | 생성 후보의 확률 범위 조절 | | `max_tokens` | integer | 최대 생성 토큰 수 | | `max_completion_tokens` | integer | 지원 모델의 출력 예산. `max_tokens`와 하나만 사용 | | `frequency_penalty` | number | 반복 표현 억제 | | `presence_penalty` | number | 새로운 주제를 다루는 성향 조절 | | `n` | integer | 생성할 응답 후보 수 | | `stream` | boolean | SSE 스트리밍 요청 | | `stream_options.include_usage` | boolean | 스트림 사용량 요청 | | `stop` | string 또는 string[] | 생성 중단 문자열 | | `seed` | integer | 재현성 보조. 동일 답변 보장 아님 | | `response_format` | object | JSON object 또는 JSON Schema 출력 요청 | | `logprobs` | boolean | 출력 토큰의 로그 확률 요청 | | `top_logprobs` | integer | 상위 로그 확률 후보 수 | **Tools · Reasoning** | Parameter | Type | 설명 | | --- | --- | --- | | `tools` | array | 함수 등 지원되는 도구 정의 | | `tool_choice` | string 또는 object | 도구 선택 방식 | | `parallel_tool_calls` | boolean | 병렬 도구 호출 허용 여부 | | `reasoning_effort` | string | 지원 모델의 추론 수준 요청 | | 기능 | 사용 시 확인 사항 | | --- | --- | | Function tool | 앱이 인자·권한을 검증하고 실행. assistant의 호출과 같은 `tool_call_id`의 결과를 다음 요청에 포함 | | 여러 tool 호출 | 각 호출 ID에 맞는 결과 전달. 결제·발송 등은 중복 실행 방지 | | Reasoning | 허용 effort 값·효과는 모델별로 다름. `message.reasoning`·`delta.reasoning`은 제공되는 경우에만 읽기 | | JSON object | `response_format.type: json_object`와 메시지의 JSON 출력 지침 사용 | | JSON Schema | `response_format.type: json_schema`의 스키마·strict 지원 확인 | 거절·길이 제한·오류를 확인한 뒤 JSON을 파싱하고 스키마를 검증하세요. 추론 토큰이 출력 예산에 포함될 수 있습니다. **Tracking** | Parameter | Type | 설명 | | --- | --- | --- | | `metadata` | object | 추가 정보. 프로바이더에 전달될 수 있으므로 비밀값 제외 | | `user` | string | 이메일·전화번호 대신 불투명한 앱 식별값 권장 | 요청별 Fallback 체인(`models`), 프리셋·캐시·자동 선택 후보 제어 옵션은 공통 지원 파라미터로 사용하지 마세요. ### 파라미터 조합 시 주의사항 | 항목 | 사용 조건 | | --- | --- | | 출력 토큰 옵션 | `max_tokens`·`max_completion_tokens` 중 하나 사용 | | `top_logprobs` | `logprobs: true`와 함께 사용. 허용 개수는 모델별 확인 | | `n` | 복수 응답을 지원하는 모델에서 사용. `choices[].index`별로 결과 처리 | | 고급 기능 | Reasoning, 구조화 출력, tool 호출의 모델별 지원 여부 확인 | ## Fallback 최초 호출이 실패하면 Space에 설정된 대체 모델로 처리를 시도할 수 있습니다. | 항목 | 안내 | | --- | --- | | 적용 API | Chat Completions·Messages | | 사용 조건 | Space의 Fallback 설정 필요 | | 대상 오류 | 서비스에서 정한 연동 오류. 모든 `5xx`가 자동 대체 대상은 아님 | | 대체 호출 성공 | 대체 모델의 응답 반환 | | 대체 호출 실패 | 최종 오류 반환 | 동일 요청이 복수 프로바이더에서 처리될 수 있습니다. # 운영 ## 데이터·가드레일·리전 ### 데이터 저장 `store: false`를 지정해도 서비스 자체의 로그·보관 정책은 그대로 적용됩니다. ### 가드레일 가드레일은 Space 단위로 적용되며, 요청 파라미터로 끄거나 변경할 수 없습니다. | 기능 | 적용 지점 | 설명 | | --- | --- | --- | | **PII** | Input / Output | 개인식별정보 마스킹 또는 차단 | | **Content Safety Guard** | Output | 유해·부적절한 생성 결과 검사 | | **Custom Block Filter** | Input / Output | 조직에서 지정한 차단 문구·패턴 | | **Adversarial Prompt Guard** | Input | 프롬프트 인젝션·탈옥 시도 탐지 | | **Secret 탐지** | Input / Output | API Key·접근 토큰 등 비밀값 탐지 | | 처리 방식 | 설명 | | --- | --- | | REDACT | 마스킹한 뒤 처리를 계속 | | BLOCK | `403 TF-E403-01` 반환 | 차단이나 마스킹이 프로바이더 처리 비용을 취소하지 않습니다. ### 처리 리전 모델명·프로바이더 포함 ID만으로 처리 리전이 보장되지는 않습니다. 처리 리전은 모델 서비스 안내나 관리자에게 확인하세요. ## 레이트리밋 · 한도 ### 한도 적용 범위 | 항목 | 의미 | | --- | --- | | 서비스 사용 한도 | API Key·Space·소속 서비스의 사용량·비용 제한 | | 프로바이더 한도 | 모델 제공 서비스의 처리 제한 | | RPM / TPM | 분당 요청 수 / 분당 토큰 수 | | 수치·해제 조건 | 모델과 서비스별 사용 안내 확인 | ### 레이트리밋 초과 응답 ```json { "error": { "message": "요청 한도를 초과했습니다", "type": "rate_limit_error", "code": "TF-E429-01" } } ``` | 원인 | 조치 | | --- | --- | | 일시적인 요청 제한 | 대기 후 제한적으로 재시도 | | 사용량·비용 상한 | 반복 호출을 멈추고 갱신 시점·한도 조정 조건 확인 | ### 응답 헤더와 재시도 | 항목 | 처리 | | --- | --- | | `Retry-After` 있음 | 지정 시간 이상 대기. 허용 대기시간을 넘으면 작업을 미룸 | | `Retry-After` 없음 | 지수 백오프 + 지터 사용 | | 앱·SDK 재시도 | 한 곳에서 관리하고 최대 횟수·전체 시간 제한 | | 대량 호출 | 작업 큐·동시성 상한을 두고 트래픽을 점진적으로 증가 | `Retry-After`는 포함된 경우에 사용합니다. 모든 `429`에 포함되는 것은 아닙니다. ## 스트리밍(SSE) ### 요청 방법 `stream: true`를 지정합니다. usage가 필요하면 `stream_options.include_usage: true`도 지정하고, curl에서는 `-N`을 사용합니다. ```bash curl -N "${TOKENFACTORY_BASE_URL}/chat/completions" \ -H "Authorization: Bearer ${TOKENFACTORY_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "모델_ID", "messages": [{"role": "user", "content": "짧은 이야기를 작성해 주세요."}], "stream": true, "stream_options": {"include_usage": true} }' ``` ### delta 청크 raw 예시 ```text data: {"id":"chatcmpl-example","object":"chat.completion.chunk","model":"model-id","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]} data: {"id":"chatcmpl-example","object":"chat.completion.chunk","model":"model-id","choices":[{"index":0,"delta":{"content":"안녕하세요."},"finish_reason":null}]} data: {"id":"chatcmpl-example","object":"chat.completion.chunk","model":"model-id","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: {"id":"chatcmpl-example","object":"chat.completion.chunk","model":"model-id","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":9,"total_tokens":21}} data: [DONE] ``` | 수신 항목 | 처리 | | --- | --- | | `delta.content`·`delta.reasoning` | 제공되는 값을 각각 누적 | | `delta.tool_calls` | 선택지·도구 index별로 `function.arguments`를 연결하고, 수신이 끝난 뒤 파싱·검증 | | `finish_reason` | 선택지의 생성 종료. 이후 usage 청크가 올 수 있으므로 수신을 계속 | | `choices: []` | 사용량 전용 청크일 수 있음 | | `error` 객체 | 실패로 처리하고 이미 받은 일부 답변을 완료 결과로 저장하지 않음 | | `[DONE]` | Chat Completions 스트림 종료. 앞선 오류가 있으면 실패 | 네트워크 청크와 SSE event 경계는 다를 수 있습니다. 직접 파싱할 때는 빈 줄로 구분한 event와 여러 `data:` 줄을 처리하세요. 클라이언트 연결 종료가 프로바이더 처리를 즉시 중단하지 않을 수 있으며, 비용 중단을 보장하지 않습니다. ### API별 SSE 형식 | 항목 | Responses | Messages | | --- | --- | --- | | 텍스트 | `response.output_text.delta` | `content_block_delta`의 `delta.text` | | 종료 | `response.completed` | `message_stop` | | 실패 | `response.failed`·`response.incomplete` | `error` event | Responses·Messages API에는 Chat Completions의 `[DONE]` 규칙을 적용하지 마세요. ## `usage` 필드 | 항목 | Chat Completions | Responses | Messages | | --- | --- | --- | --- | | 입력 | `prompt_tokens` | `input_tokens` | `input_tokens` | | 출력 | `completion_tokens` | `output_tokens` | `output_tokens` | | 합계 | `total_tokens` | `total_tokens` | 프로바이더별 상세와 함께 확인 | | 캐시 입력 | `prompt_tokens_details.cached_tokens` (제공 시) | — | — | | 추론 출력 | `completion_tokens_details.reasoning_tokens` (제공 시) | — | — | 상세 토큰은 상위 토큰 수에 포함될 수 있으므로 합계에 다시 더하지 마세요. `usage`는 토큰 정보이며 청구 금액 자체는 아닙니다. 스트리밍에서 usage를 받으려면 `stream_options.include_usage: true`를 지정합니다. `—`는 이 문서에서 해당 API의 필드를 안내하지 않음을 뜻합니다. ## 프로바이더 지원 매트릭스 같은 프로바이더에서도 모델과 서비스 구성에 따라 지원 기능이 다릅니다. 실제 모델 서비스 안내를 기준으로 사용하세요. | API | 사용할 대상 | 주요 조건 | | --- | --- | --- | | Chat Completions | 여러 모델의 공통 호출 | 변환·고급 옵션 지원 확인 | | Responses | Responses 형식 지원 모델 | 상태 기능 제한 확인 | | Messages | Messages 형식 지원 모델 | 고유 content·tool·SSE 형식 사용 | | 기능 | 확인할 내용 | | --- | --- | | 스트리밍 | 지원 여부, API별 종료·오류 event | | Function Calling | 도구 정의·선택·병렬 호출 지원 | | JSON mode / JSON Schema | 각 방식과 strict 옵션 지원 | | Reasoning | 허용 effort 값, 추론 정보 제공 여부 | | `logprobs` / `seed` | 모델별 지원 범위 | | 문맥·출력 길이 | 입력·출력 예산과 추론 토큰 포함 여부 | SDK에 있는 파일 관리·저장 응답 조회·서버 대화 상태 등의 API가 모두 제공되는 것은 아닙니다. # SDK ## SDK 연동 OpenAI 공식 SDK에 KT 토큰팩토리의 Base URL·키·모델 ID를 설정합니다. 예제에서는 SDK 자동 재시도를 끕니다(`max_retries=0`). ### Python ```bash python -m pip install openai ``` #### 일반 호출 ```python import os from openai import OpenAI client = OpenAI( base_url=os.environ["TOKENFACTORY_BASE_URL"], api_key=os.environ["TOKENFACTORY_API_KEY"], timeout=60.0, max_retries=0, ) resp = client.chat.completions.create( model=os.environ["TOKENFACTORY_MODEL"], messages=[ {"role": "system", "content": "릴리스 노트를 세 항목으로 정리하세요."}, {"role": "user", "content": "로그인 속도 개선, 다크모드 추가, 결제 오류 수정"}, ], ) print(resp.choices[0].message.content or "") ``` #### 스트리밍 위의 `client`를 사용하고 usage 전용 청크를 답변 텍스트와 구분합니다. ```python usage = None finish_reason = None refused = False with client.chat.completions.create( model=os.environ["TOKENFACTORY_MODEL"], messages=[{"role": "user", "content": "짧은 이야기를 작성해 주세요."}], stream=True, stream_options={"include_usage": True}, ) as stream: for chunk in stream: if chunk.usage is not None: usage = chunk.usage for choice in chunk.choices: if choice.index != 0: continue if choice.delta.content: print(choice.delta.content, end="", flush=True) refused = refused or bool(choice.delta.refusal) if choice.finish_reason is not None: finish_reason = choice.finish_reason if refused or finish_reason != "stop": raise RuntimeError(f"응답 거절 또는 미완료: {finish_reason}") print() if usage is not None: print("사용량:", usage) ``` ### Node.js ```bash npm install openai ``` 다음 코드는 `.mjs` 또는 ES module 환경에서 실행합니다. #### 일반 호출 ```javascript import OpenAI from "openai"; const client = new OpenAI({ baseURL: process.env.TOKENFACTORY_BASE_URL, apiKey: process.env.TOKENFACTORY_API_KEY, timeout: 60_000, maxRetries: 0, }); const response = await client.chat.completions.create({ model: process.env.TOKENFACTORY_MODEL, messages: [{ role: "user", content: "AI Gateway를 한 문장으로 설명해 주세요." }], }); console.log(response.choices[0]?.message.content ?? ""); ``` SDK 사용법: [Python](https://developers.openai.com/api/reference/python) · [JavaScript](https://developers.openai.com/api/reference/typescript) # 오류 코드 ## 오류 코드 다음은 Chat Completions의 오류 형식입니다. 다른 API는 `type`과 부가 필드가 다를 수 있으므로 **HTTP 상태와 `error.code`**를 함께 확인하세요. ```json { "error": { "message": "오류 설명", "type": "error_type", "code": "error_code", "param": null } } ``` 문의 시 요청 시각·시간대, API 경로·모델 ID, HTTP 상태·오류 코드와 응답에 포함된 `X-TRAN-ID`를 전달하세요. API Key와 민감한 입력·출력은 제거합니다. ### KT Token Factory 오류 코드 **재시도 표시:** ❌ 동일 요청 자동 반복 금지 / ✅ 일시 장애이며 중복 처리에 안전한 요청에 한해 제한적으로 재시도 | 에러 코드 | HTTP | 의미 | 재시도 | 확인·조치 | | --- | ---: | --- | :---: | --- | | `TF-E400-01` | 400 | 요청 값·형식 오류 | ❌ | JSON, 필수값·타입 수정 | | `TF-E400-02` | 400 | 모델 사용 불가 가능 | ❌ | 모델 ID·사용 조건 확인 | | `TF-E400-03` | 400 | 프로바이더 사용 불가 가능 | ❌ | 프로바이더 포함 ID·사용 조건 확인 | | `TF-E401-01` | 401 | 인증 실패 | ❌ | Bearer 형식, 키 상태·기간 확인 | | `TF-E403-01` | 403 | 정책 차단 | ❌ | 요청 내용·사용 조건 확인 | | `TF-E404-01` | 404 | API를 찾을 수 없음 | ❌ | Base URL·경로·HTTP method 확인 | | `TF-E422-01` | 422 | 대상 AI 규격으로 변환 불가 | ❌ | 요청 형식 확인 | | `TF-E499-01` | 499 | 클라이언트 취소 | ❌ | 사용자 의도·client timeout 확인 | | `TF-E500-01` | 500 | 서버 처리 오류 | ✅ | 대기 후 재시도. 반복되면 문의 | | `TF-E502-01` | 502 | AI 프로바이더 연동 실패 | ✅ | 중복 처리 가능성 확인 후 재시도 | | `TF-E503-01` | 503 | 일시적으로 서비스 이용 불가 | ✅ | 대기 후 재시도. 지속되면 문의 | | `TF-E504-01` | 504 | AI 프로바이더 응답 시간 초과 | ✅ | 대기시간·요청 크기 확인 후 재시도 | 네트워크 오류·timeout은 이미 처리된 요청일 수 있습니다. 일부 스트림을 받은 뒤에는 자동 재실행하지 말고 중복 가능성을 먼저 확인하세요. ### 프로바이더 오류 프로바이더가 반환한 오류에는 실제 HTTP 상태를 포함한 `PV-E{StatusCode}`가 사용될 수 있습니다. | 에러 코드 | 재시도 | 확인·조치 | | --- | :---: | --- | | `PV-E500`·`PV-E503` 등 5xx | ✅ | 일시 장애이면 제한적으로 재시도 |