Compare commits
1 Commits
feature-p2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9aa4e0a343 |
@ -6,6 +6,10 @@ from app.utils.prompts.schemas import SpaceType, Subject, Camera, MotionRecommen
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
# medium 추론은 출력 비용의 80%를 차지하고, minimal은 A/B 비교(4회)에서 narrative 점수가
|
||||||
|
# welcome 단계로 편향되고 태그를 과다 선택하는 패턴이 반복돼 low로 고정한다.
|
||||||
|
IMAGE_TAG_REASONING_EFFORT = "low"
|
||||||
|
|
||||||
async def autotag_image(image_url : str, industry: str = "") -> list[str]: #tag_list
|
async def autotag_image(image_url : str, industry: str = "") -> list[str]: #tag_list
|
||||||
chatgpt = ChatgptService(model_type="gpt")
|
chatgpt = ChatgptService(model_type="gpt")
|
||||||
image_input_data = {
|
image_input_data = {
|
||||||
@ -17,7 +21,7 @@ async def autotag_image(image_url : str, industry: str = "") -> list[str]: #tag_
|
|||||||
"motion_recommended" : list(MotionRecommended)
|
"motion_recommended" : list(MotionRecommended)
|
||||||
}
|
}
|
||||||
|
|
||||||
image_result = await chatgpt.generate_structured_output(image_autotag_prompt, image_input_data, image_url, False)
|
image_result = await chatgpt.generate_structured_output(image_autotag_prompt, image_input_data, image_url, True, reasoning_effort=IMAGE_TAG_REASONING_EFFORT)
|
||||||
return image_result
|
return image_result
|
||||||
|
|
||||||
async def autotag_images(image_url_list : list[str], industry: str = "") -> list[dict]: #tag_list
|
async def autotag_images(image_url_list : list[str], industry: str = "") -> list[dict]: #tag_list
|
||||||
@ -31,7 +35,7 @@ async def autotag_images(image_url_list : list[str], industry: str = "") -> list
|
|||||||
"motion_recommended" : list(MotionRecommended)
|
"motion_recommended" : list(MotionRecommended)
|
||||||
}for image_url in image_url_list]
|
}for image_url in image_url_list]
|
||||||
|
|
||||||
image_result_tasks = [chatgpt.generate_structured_output(image_autotag_prompt, image_input_data, image_input_data['img_url'], False, silent = True) for image_input_data in image_input_data_list]
|
image_result_tasks = [chatgpt.generate_structured_output(image_autotag_prompt, image_input_data, image_input_data['img_url'], True, silent = True, reasoning_effort=IMAGE_TAG_REASONING_EFFORT) for image_input_data in image_input_data_list]
|
||||||
image_result_list: list[BaseModel | BaseException] = await asyncio.gather(*image_result_tasks, return_exceptions=True)
|
image_result_list: list[BaseModel | BaseException] = await asyncio.gather(*image_result_tasks, return_exceptions=True)
|
||||||
MAX_RETRY = 2
|
MAX_RETRY = 2
|
||||||
for _ in range(MAX_RETRY):
|
for _ in range(MAX_RETRY):
|
||||||
@ -40,7 +44,7 @@ async def autotag_images(image_url_list : list[str], industry: str = "") -> list
|
|||||||
if not failed_idx:
|
if not failed_idx:
|
||||||
break
|
break
|
||||||
retried = await asyncio.gather(
|
retried = await asyncio.gather(
|
||||||
*[chatgpt.generate_structured_output(image_autotag_prompt, image_input_data_list[i], image_input_data_list[i]['img_url'], False, silent=True) for i in failed_idx],
|
*[chatgpt.generate_structured_output(image_autotag_prompt, image_input_data_list[i], image_input_data_list[i]['img_url'], True, silent=True, reasoning_effort=IMAGE_TAG_REASONING_EFFORT) for i in failed_idx],
|
||||||
return_exceptions=True
|
return_exceptions=True
|
||||||
)
|
)
|
||||||
for i, result in zip(failed_idx, retried):
|
for i, result in zip(failed_idx, retried):
|
||||||
|
|||||||
@ -47,6 +47,19 @@ class ChatgptService:
|
|||||||
case _:
|
case _:
|
||||||
raise NotImplementedError(f"Unknown Provider : {model_type}")
|
raise NotImplementedError(f"Unknown Provider : {model_type}")
|
||||||
|
|
||||||
|
def _log_usage(self, response, model: str, output_format: type[BaseModel]) -> None:
|
||||||
|
usage = getattr(response, "usage", None)
|
||||||
|
if usage is None:
|
||||||
|
return
|
||||||
|
# 토큰 소모량 로깅 (필요 시 주석 해제)
|
||||||
|
# cached = getattr(getattr(usage, "prompt_tokens_details", None), "cached_tokens", None) or 0
|
||||||
|
# reasoning = getattr(getattr(usage, "completion_tokens_details", None), "reasoning_tokens", None) or 0
|
||||||
|
# logger.info(
|
||||||
|
# f"[ChatgptService({self.model_type})] usage model={model} output={output_format.__name__} "
|
||||||
|
# f"prompt={usage.prompt_tokens} cached={cached} "
|
||||||
|
# f"completion={usage.completion_tokens} reasoning={reasoning} total={usage.total_tokens}"
|
||||||
|
# )
|
||||||
|
|
||||||
async def _call_pydantic_output(
|
async def _call_pydantic_output(
|
||||||
self,
|
self,
|
||||||
prompt : str,
|
prompt : str,
|
||||||
@ -115,7 +128,8 @@ class ChatgptService:
|
|||||||
output_format : BaseModel, #입력 output_format의 경우 Pydantic BaseModel Class를 상속한 Class 자체임에 유의할 것
|
output_format : BaseModel, #입력 output_format의 경우 Pydantic BaseModel Class를 상속한 Class 자체임에 유의할 것
|
||||||
model : str,
|
model : str,
|
||||||
img_url : str,
|
img_url : str,
|
||||||
image_detail_high : bool) -> BaseModel:
|
image_detail_high : bool,
|
||||||
|
reasoning_effort : Optional[str] = None) -> BaseModel:
|
||||||
content = []
|
content = []
|
||||||
if img_url:
|
if img_url:
|
||||||
content.append({
|
content.append({
|
||||||
@ -129,13 +143,16 @@ class ChatgptService:
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"text": prompt
|
"text": prompt
|
||||||
})
|
})
|
||||||
|
# gpt-5.4 계열/Gemini 호환 엔드포인트는 허용 값이 다르거나 파라미터를 거부하므로 지정된 경우에만 전달
|
||||||
|
extra_kwargs = {"reasoning_effort": reasoning_effort} if reasoning_effort else {}
|
||||||
last_error = None
|
last_error = None
|
||||||
for attempt in range(self.max_retries + 1):
|
for attempt in range(self.max_retries + 1):
|
||||||
try:
|
try:
|
||||||
response = await self.client.beta.chat.completions.parse(
|
response = await self.client.beta.chat.completions.parse(
|
||||||
model=model,
|
model=model,
|
||||||
messages=[{"role": "user", "content": content}],
|
messages=[{"role": "user", "content": content}],
|
||||||
response_format=output_format
|
response_format=output_format,
|
||||||
|
**extra_kwargs,
|
||||||
)
|
)
|
||||||
except (ValidationError, json.JSONDecodeError) as e:
|
except (ValidationError, json.JSONDecodeError) as e:
|
||||||
# 모델이 스키마에 맞지 않는 JSON을 반환한 경우 (예: trailing characters).
|
# 모델이 스키마에 맞지 않는 JSON을 반환한 경우 (예: trailing characters).
|
||||||
@ -148,6 +165,7 @@ class ChatgptService:
|
|||||||
if attempt < self.max_retries:
|
if attempt < self.max_retries:
|
||||||
logger.info(f"[ChatgptService({self.model_type})] Retrying request...")
|
logger.info(f"[ChatgptService({self.model_type})] Retrying request...")
|
||||||
continue
|
continue
|
||||||
|
self._log_usage(response, model, output_format)
|
||||||
# Response 디버그 로깅
|
# Response 디버그 로깅
|
||||||
# logger.debug(f"[ChatgptService({self.model_type})] attempt: {attempt}")
|
# logger.debug(f"[ChatgptService({self.model_type})] attempt: {attempt}")
|
||||||
# logger.debug(f"[ChatgptService({self.model_type})] Response ID: {response.id}")
|
# logger.debug(f"[ChatgptService({self.model_type})] Response ID: {response.id}")
|
||||||
@ -225,6 +243,7 @@ class ChatgptService:
|
|||||||
continue
|
continue
|
||||||
raise last_error
|
raise last_error
|
||||||
|
|
||||||
|
self._log_usage(response, model, output_format)
|
||||||
choice = response.choices[0]
|
choice = response.choices[0]
|
||||||
if choice.finish_reason == "stop":
|
if choice.finish_reason == "stop":
|
||||||
return choice.message.parsed
|
return choice.message.parsed
|
||||||
@ -242,7 +261,8 @@ class ChatgptService:
|
|||||||
input_data : dict,
|
input_data : dict,
|
||||||
img_url : Optional[str] = None,
|
img_url : Optional[str] = None,
|
||||||
img_detail_high : bool = False,
|
img_detail_high : bool = False,
|
||||||
silent : bool = True
|
silent : bool = True,
|
||||||
|
reasoning_effort : Optional[str] = None,
|
||||||
) -> BaseModel:
|
) -> BaseModel:
|
||||||
prompt_text = prompt.build_prompt(input_data, silent)
|
prompt_text = prompt.build_prompt(input_data, silent)
|
||||||
|
|
||||||
@ -253,5 +273,5 @@ class ChatgptService:
|
|||||||
# GPT API 호출
|
# GPT API 호출
|
||||||
#parsed = await self._call_structured_output_with_response_gpt_api(prompt_text, prompt.prompt_output, prompt.prompt_model)
|
#parsed = await self._call_structured_output_with_response_gpt_api(prompt_text, prompt.prompt_output, prompt.prompt_model)
|
||||||
# parsed = await self._call_pydantic_output(prompt_text, prompt.prompt_output_class, prompt.prompt_model, img_url, img_detail_high)
|
# parsed = await self._call_pydantic_output(prompt_text, prompt.prompt_output_class, prompt.prompt_model, img_url, img_detail_high)
|
||||||
parsed = await self._call_pydantic_output_chat_completion(prompt_text, prompt.prompt_output_class, prompt.prompt_model, img_url, img_detail_high)
|
parsed = await self._call_pydantic_output_chat_completion(prompt_text, prompt.prompt_output_class, prompt.prompt_model, img_url, img_detail_high, reasoning_effort)
|
||||||
return parsed
|
return parsed
|
||||||
Loading…
Reference in New Issue
Block a user