type object 'dummy' has no attribute 'model_json_schema' 오류 해결
환경
google-generativeai라이브러리를 통해 gemini로 요청을 보내는 과정에서 오류가 발생하였다.
사용한 버전은 아래와 같다.
google-generativeai==0.8.3
pydantic==1.1.0
오류는 generate_content_async 메서드에서
응답 받을 스키마 형태인 response_schema에 pydantic 객체를 넣어주는 과정에서 문제가 발생하였다.
원인 파악
response = await self.model.generate_content_async(
query,
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
response_schema=Category
temperature=0,
),
safety_settings={
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
},
)
pydantic 객체를 넣어주면, gemini 라이브러리 내부 로직에서 pydantic의 model_json_schema 메서드를 통해 응답 값을 만들어주는데, 이때 model_json_schema 메서드가 존재하지 않는 것이 원인이었다.
model_json_schema 메서드는 pydantic v2부터 지원하기 때문에, pydantic v1을 사용하는 입장에서 오류가 발생한 것이다.
문제 해결
- pydantic version을 v2로 마이그레이션
- response_schema 형식에 맞게 schema 직접 제공
이미 프로젝트가 거의 완성되었고, 다른 의존성들끼리 이미 버전을 맞춘 상황에서 마이그레이션 방법은 지금 당장 비용이 많이 발생할 것으로 판단하였다. 따라서 2번 방법을 선택하였다.
먼저 response_schema에 삽입할 pydantic 객체에 json_schema 메서드를 만들었다.
pydantic BaseModel의 schema 메서드를 통해 데이터를 가져온 후 가공 작업을 거쳤다.
그대로 값을 사용하게 되면 오류가 발생하게 되는데, gemini에서 title key값을 허용하지 않아서 발생한 오류였다. 따라서 딕셔너리의 모든 값에 대해서 key값을 제거하는 과정을 거쳤다.
schema 메서드 생성
class Category(BaseModel):
categories: list[str]
@classmethod
def json_schema(cls):
# Pydantic v1 기준 schema() 메서드 사용
schema = cls.schema()
def remove_titles(data):
if isinstance(data, dict):
# 현재 딕셔너리에서 "title" 키 제거
data.pop("title", None)
# 딕셔너리의 모든 값에 대해 재귀적으로 처리
for key, value in data.items():
remove_titles(value)
elif isinstance(data, list):
# 리스트 내 각 항목에 대해 재귀적으로 처리
for item in data:
remove_titles(item)
return data
return remove_titles(schema)
추가된 메서드 활용
response = await self.model.generate_content_async(
query,
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
response_schema=Category.json_schema()
temperature=0,
),
safety_settings={
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
},
)