티스토리 뷰
오랜만에 GPT 관련 글을 쓰는 것 같다. 마지막 글이 2월이니 넉달만에 쓰게 됐다.
이 사이 GPT4o가 나오고, assistant가 v2로 업데이트되었고, 여러 모델들이 업데이트 되었다.
AI가 천천히 일상에 스며들고 있는데, 개발하는 입장에서는 좋은 소식이 4월 중 몇가지 들렸다.
사실 GPT4 Model은 API 사용료가 꽤 비싼데,
GPT4o 모델을 제공한다는 것과 Batch API를 통해 훨씬 저렴하게 API를 사용할 수 있게 됐다.
하지만 Batch API는 사용 방법이 복잡하고, 제한적이라 정리가 필요할 것 같아서 정리를 하고 가려고 한다.
그렇지만 못할 정도는 아니고 API 개수가 몇 개 안되니가 스텝별로 진행해보자.
1. Create Batch
https://platform.openai.com/docs/api-reference/batch/create
Batch API를 사용하기 위해서는, 먼저 Files API를 알아야 한다. 규격에 맞게 File을 업로드해야한다.
Create File API를 이용해 규격에 맞는 파일을 써야하고, purpose도 batch로 지정한다.
// batch 규격에 맞는 JSON 파일
{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo-0125", "messages": [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Hello world!"}],"max_tokens": 1000}}
{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo-0125", "messages": [{"role": "system", "content": "You are an unhelpful assistant."},{"role": "user", "content": "Hello world!"}],"max_tokens": 1000}}
파일이 정상적으로 Create되면, 아래와 같이 ID를 얻을 수 있다.
이 ID와 endpoint와 completion_window를 파라미터로 받는 Batches API를 만든다.
completion_window는 전체 batch가 끝나는 시간을 제한해둔 변수이다. 현재는 24h밖에 사용하지 못 한다.
늘 사용하던데로 FeignClient를 사용했다.
Request
data class BatchesCreateDto (
@JsonProperty("input_file_id")
val inputFileId: String,
val endpoint: String,
@JsonProperty("completion_window")
val completionWindow: String
) {
constructor(createBatchesRequest: CreateBatchesRequest) : this(
inputFileId = createBatchesRequest.inputFileId,
endpoint = createBatchesRequest.endpoint,
completionWindow = createBatchesRequest.completionWindow
)
}
// file도 필요하니 코드에 포함
@FeignClient(name = "OpenAiClient", url = "https://api.openai.com/v1", configuration = [OpenAiHeaderConfiguration::class])
interface OpenAiFeignClient {
@PostMapping("/files", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun uploadFile(@RequestPart file: MultipartFile, @RequestPart("purpose") purpose: String): Any
@PostMapping("/batches")
fun createBatches(@RequestBody batchesCreateDto: BatchesCreateDto): Any
}
Response
{
"id": "batch_J52fTEP5cz30kXXj9bvwrLY7",
"object": "batch",
"endpoint": "/v1/chat/completions",
"errors": null,
"input_file_id": "file-LVMBPSLw2kcIjw5tDGPE3jNq",
"completion_window": "24h",
"status": "validating",
"output_file_id": null,
"error_file_id": null,
"created_at": 1717746791,
"in_progress_at": null,
"expires_at": 1717833191,
"finalizing_at": null,
"completed_at": null,
"failed_at": null,
"expired_at": null,
"cancelling_at": null,
"cancelled_at": null,
"request_counts": {
"total": 0,
"completed": 0,
"failed": 0
},
"metadata": null
}
2. Retrieve batch
Create의 Response를 확인해보면 batch_J52fTEP5cz30kXXj9bvwrLY7 라는 Batch ID를 얻을 수 있다.
이 ID를 이용해 Batch의 상태를 조회해 볼 수 있다.
아마 데이터가 두 개뿐이라 글을 작성하는 동안 완료가 됐을 것이다.
Request
@GetMapping("/batches/{batchId}")
fun getBatchesList(@PathVariable batchId: String) : Any
Response
{
"id": "batch_J52fTEP5cz30kXXj9bvwrLY7",
"object": "batch",
"endpoint": "/v1/chat/completions",
"errors": null,
"input_file_id": "file-LVMBPSLw2kcIjw5tDGPE3jNq",
"completion_window": "24h",
"status": "in_progress",
"output_file_id": null,
"error_file_id": null,
"created_at": 1717746791,
"in_progress_at": 1717746792,
"expires_at": 1717833191,
"finalizing_at": null,
"completed_at": null,
"failed_at": null,
"expired_at": null,
"cancelling_at": null,
"cancelled_at": null,
"request_counts": {
"total": 2,
"completed": 1,
"failed": 0
},
"metadata": null
}
작업을 2개 걸어뒀는데, 하나만 끝나서 현재 status가 in_progress로 출력된다.
3. List batch
현재 API Key를 사용하는 모든 batch의 list를 보여준다.
마침 타이밍 좋게 두 개 다 종료가 됐다.
Request
@GetMapping("/batches")
fun getBatchList() : Any
Response
{
"object": "list",
"data": [
{
"id": "batch_J52fTEP5cz30kXXj9bvwrLY7",
"object": "batch",
"endpoint": "/v1/chat/completions",
"errors": null,
"input_file_id": "file-LVMBPSLw2kcIjw5tDGPE3jNq",
"completion_window": "24h",
"status": "completed",
"output_file_id": "file-BvpH9Eej1MRyaDjyVo4ilzUL",
"error_file_id": null,
"created_at": 1717746791,
"in_progress_at": 1717746792,
"expires_at": 1717833191,
"finalizing_at": 1717748144,
"completed_at": 1717748144,
"failed_at": null,
"expired_at": null,
"cancelling_at": null,
"cancelled_at": null,
"request_counts": {
"total": 2,
"completed": 2,
"failed": 0
},
"metadata": null
}
],
"first_id": "batch_J52fTEP5cz30kXXj9bvwrLY7",
"last_id": "batch_J52fTEP5cz30kXXj9bvwrLY7",
"has_more": false
}
4. 결과 확인
결과확인은 두 가지 방법으로 할 수 있다.
첫 번째는 OpenAI Playground에 접속해서 다운로드해서 확인하는 것
두 번째는 조금 귀찮긴 하지만 /files/{file_id}/content API로 확인해 볼 수 있다.
@FeignClient(name = "OpenAiClient", url = "https://api.openai.com/v1", configuration = [OpenAiHeaderConfiguration::class])
interface OpenAiFeignClient {
@GetMapping("/files/{fileId}/content")
fun getFilesContent(@PathVariable fileId: String): ByteArray
}
fun getFilesContent(fileId: String): ResponseEntity<Any> {
return try {
val uploadResult = openAiFeignClient.getFilesContent(fileId)
ResponseEntity.ok(saveFile(uploadResult, "outputFile.json"))
} catch (e: Exception) {
ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
}
}
fun saveFile(byteArray: ByteArray, filePath: String): String {
val file = File(filePath)
file.writeBytes(byteArray)
return "File saved successfully at $filePath"
}
outputFile.json을 열어보면 아래와 같은 결과를 확인해 볼 수 있다.
{"id": "batch_req_BgPY4YQ3SZKIIraS3mi3LXKD", "custom_id": "request-1", "response": {"status_code": 200, "request_id": "9ab692590d97c6d8f4eae8cd6bf511a4", "body": {"id": "chatcmpl-9XOixW6L21pHgrABo1FNWuUNqqWVS", "object": "chat.completion", "created": 1717746891, "model": "gpt-3.5-turbo-0125", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Hello! How can I assist you today?"}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 20, "completion_tokens": 9, "total_tokens": 29}, "system_fingerprint": null}}, "error": null}
{"id": "batch_req_uepcDhYJTcWIHurmA9FgxjaF", "custom_id": "request-2", "response": {"status_code": 200, "request_id": "fe4ecf39607983548008df2efac35f4d", "body": {"id": "chatcmpl-9XOimMdUsAyEZkt5GopN7RIuNzTHS", "object": "chat.completion", "created": 1717746880, "model": "gpt-3.5-turbo-0125", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Hello! How can I not assist you today?"}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 22, "completion_tokens": 10, "total_tokens": 32}, "system_fingerprint": null}}, "error": null}
5. Cancel batch
마지막으로 batch를 중지하는 API이다. in-progress 중일 때 이 API를 호출하면 status가 cancelling이 되고, 10분이 지나면 cancelled가 된다.
Request
@PostMapping("/batches/{batchId}/cancel")
fun cancelBatches(@PathVariable batchId: String) : Any
Response
{
"id": "batch_85AKedsacbOsGY2vJjAgfVLQ",
"object": "batch",
"endpoint": "/v1/chat/completions",
"errors": null,
"input_file_id": "file-LVMBPSLw2kcIjw5tDGPE3jNq",
"completion_window": "24h",
"status": "cancelling", // 10분 후 cancelled가 됨
"output_file_id": null,
"error_file_id": null,
"created_at": 1717751256,
"in_progress_at": 1717751256,
"expires_at": 1717837656,
"finalizing_at": null,
"completed_at": null,
"failed_at": null,
"expired_at": null,
"cancelling_at": 1717751266,
"cancelled_at": null,
"request_counts": {
"total": 2,
"completed": 0,
"failed": 0
},
"metadata": null
}
완료된 Batch ID를 넣으면 아래와 같이 출력된다.
Error
[409 Conflict] during [POST] to [https://api.openai.com/v1/batches/batch_J52fTEP5cz30kXXj9bvwrLY7/cancel] [OpenAiFeignClient#cancelBatches(String)]: [{
"error": {
"message": "Cannot cancel a batch with status 'completed'.",
"type": "invalid_request_error",
"param": null,
"code": null
}
}]
마지막으로 Batch 삭제는 Playground > Storage > Batch Id 선택 > 우측 상단 쓰레기통 모양을 눌러주면 된다.
Github
마치며
오픈 소스 기여를 위해 Batch API를 써볼 필요가 있어서 포스팅까지 해봤다.
오랜만에 막힘없이 할 수 있는 CRUD 구현 작업을 하니 재밌긴하다.
다만, 이 포스팅보다는 100배는 중요하다고 생각하는 오픈 소스 기여가 잘 진행될지 모르겠다...
'개발 > chatGPT' 카테고리의 다른 글
ChatGPT에서 나만의 봇 만들기. My GPTs - Actions 사용하기 (0) | 2024.02.08 |
---|---|
ChatGPT Assistant API 사용 중 알게된 문제점 해결하기(run 대기상태 이슈, Threads 리스트 보기 - GET /v1/threads) (1) | 2023.12.18 |
ChatGPT Assistants API 사용하기 (with. SpringBoot) (2) | 2023.12.14 |
ChatGPT Builder로 나만의 챗봇 만들기 (0) | 2023.11.15 |
ChatGPT로 이미지 해석하기 - 스프링부트에서 Vision API 사용하기 (1) | 2023.11.14 |
- Total
- Today
- Yesterday
- JWT
- serverless
- docker
- Elastic cloud
- AWS EC2
- 티스토리챌린지
- GIT
- S3
- OpenFeign
- Kotlin
- AWS
- openAI API
- CloudFront
- lambda
- elasticsearch
- Spring
- ChatGPT
- Log
- 후쿠오카
- cache
- 스프링부트
- MySQL
- springboot
- java
- terraform
- OpenAI
- 오블완
- AOP
- EKS
- 람다
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |