-
Notifications
You must be signed in to change notification settings - Fork 0
chore(chatroom): add chatroom domain reference implementation #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add schemas for chatroom domain: - Chatroom, ChatroomCreate, ChatroomUpdate - Message, MessageCreate, CompanionMessage, CompanionMessageCreate - SenderType enum (USER, COMPANION) - Persona with timezone support - ModelSettings for LLM configuration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add database models for chatroom domain: - DBChatroom: name, language with indexes - DBMessage: chatroom_id FK, sender_type, content with indexes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add database-backed chatroom repository: - Extends BaseRepository for CRUD operations - add_message_to_chatroom: Create messages with auto-generated IDs - get_messages_by_chatroom_id: Paginated message retrieval Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add LLM provider abstractions: - LLMProvider Protocol for platform-agnostic LLM integration - ProviderConstraints for context building (alternating turns, etc.) - OpenAILLMProvider using LangChain ChatOpenAI - lookup_context_window using LiteLLM model_cost Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add context engineering utilities: - MessageContextBuilder: Builder pattern for LLM context assembly - Provider constraint handling (combine_system_messages, alternating turns) - format_datetime_korean: Korean datetime formatting - format_utc_offset: UTC offset string formatting - Helper functions for message validation and deduplication Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add service for TEXT response generation: - Provides response format prompts (empty for TEXT) - Generates response from prepared context via LLMProvider - Extensible for AUDIO/VIDEO response types Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add chatroom API router:
- ChatroomRouter extending BaseCrudRouter
- Public create/get chatroom endpoints
- GET /messages: Paginated message retrieval
- POST /messages: Send user message
- POST /companions/{id}/response: Generate AI response
- ChatroomRepositoryProtocol and factory
- Settings with AIDOL_OPENAI_MODEL env var
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add dependencies for chatroom domain: - langchain-core>=0.3.0: LangChain message types - langchain-openai>=0.3.0: ChatOpenAI provider - litellm>=1.60.0: Model context window lookup Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add TypeScript schemas for chatroom domain: - Chatroom, ChatroomCreate with Zod validation - Message with SenderType enum (USER, COMPANION) - Type guards: isUser, isCompanion Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Add repositories for chatroom domain: - ChatroomRepository: CRUD, message send/get, AI response generation - LocalChatroomIdsRepository: companionId → chatroomId mapping - LocalClaimTokenRepository: shared claim token storage Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Add chatroom components: - ChatRoom: Container combining MessageList and MessageInput - MessageList: Message display with markdown support, loading skeleton - MessageInput: Text input with send button Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Update entry points to export chatroom functionality: - index.ts: Export ChatroomRepository, types, and schemas - client.ts: Export ChatRoom, MessageInput, MessageList components Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Add dependencies for chatroom message rendering: - react-markdown@^9.0.0: Markdown rendering in messages - remark-breaks@^4.0.0: Line break support - remark-gfm@^4.0.0: GitHub Flavored Markdown support Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
이 PR은 Buppy에서 추출한 채팅방 도메인의 참조 구현을 추가하는 중요한 변경 사항을 담고 있습니다. 백엔드 API, 데이터베이스 모델, 서비스 및 프론트엔드 컴포넌트를 포함하여 완전한 기능을 갖춘 모듈을 도입했습니다. 전반적으로 코드 구조가 잘 잡혀 있고, 프로토콜을 사용한 의존성 주입 등 좋은 설계 패턴을 따르고 있습니다. 다만, 몇 가지 중요한 수정이 필요한 부분이 있습니다. 특히 LLM에 전달되는 대화 기록의 순서가 반대로 되어 있어 시급한 수정이 필요하며, 데이터베이스 모델과 프론트엔드 컴포넌트에서 약간의 개선이 가능합니다. 자세한 내용은 아래 주석을 참고해 주세요.
| ) | ||
|
|
||
| # Convert to LangChain BaseMessage format | ||
| langchain_messages = _to_langchain_messages(messages) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
대화 기록이 LLM에 역순으로 전달되고 있습니다. repository.get_messages_by_chatroom_id는 최신 메시지부터 가져오도록(created_at.desc()) 구현되어 있는데, 이 순서를 뒤집지 않고 _to_langchain_messages로 전달하고 있습니다. LLM은 시간 순서에 맞는 대화 기록을 받아야 정확한 맥락을 파악할 수 있으므로, 메시지 목록을 올바른 순서로 뒤집어 전달해야 합니다.
| langchain_messages = _to_langchain_messages(messages) | |
| langchain_messages = _to_langchain_messages(messages[::-1]) |
| def _convert_db_message_to_model(db_message: DBMessage) -> Message: | ||
| """Convert DB message to Pydantic model. | ||
|
|
||
| Returns CompanionMessage if sender_type is COMPANION, else Message. | ||
| """ | ||
| if db_message.sender_type == SenderType.COMPANION.value: | ||
| return CompanionMessage( | ||
| id=db_message.id, | ||
| sender_type=SenderType.COMPANION, | ||
| content=db_message.content, | ||
| created_at=db_message.created_at.replace(tzinfo=timezone.utc), | ||
| ) | ||
|
|
||
| return Message( | ||
| id=db_message.id, | ||
| sender_type=SenderType(db_message.sender_type), | ||
| content=db_message.content, | ||
| created_at=db_message.created_at.replace(tzinfo=timezone.utc), | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_convert_db_message_to_model 함수는 sender_type이 COMPANION이 아닌 모든 경우를 SenderType enum으로 변환하려고 시도합니다. 하지만 DBMessage 모델의 주석에 따르면 sender_type은 "system"이나 "unknown"일 수도 있습니다. 현재 SenderType enum에는 이 값들이 정의되어 있지 않아, 해당 값들이 DB에 저장될 경우 ValueError가 발생할 수 있습니다. 모든 가능한 sender_type 값을 처리하도록 코드를 수정해야 합니다.
def _convert_db_message_to_model(db_message: DBMessage) -> Message:
"""Convert DB message to Pydantic model.
Returns CompanionMessage if sender_type is COMPANION, else Message.
"""
sender_type_value = db_message.sender_type
if sender_type_value == SenderType.COMPANION.value:
return CompanionMessage(
id=db_message.id,
sender_type=SenderType.COMPANION,
content=db_message.content,
created_at=db_message.created_at.replace(tzinfo=timezone.utc),
)
try:
sender_type = SenderType(sender_type_value)
except ValueError:
# "system"이나 "unknown"과 같이 SenderType에 정의되지 않은 값에 대한 폴백 처리
sender_type = SenderType.USER
return Message(
id=db_message.id,
sender_type=sender_type,
content=db_message.content,
created_at=db_message.created_at.replace(tzinfo=timezone.utc),
)| className="input bg-neutral/30 text-base-content placeholder:text-base-content focus:border-primary w-full pr-12 focus:outline-hidden" | ||
| /> | ||
| <button | ||
| type="submit" | ||
| className="btn btn-circle btn-ghost text-primary hover:bg-primary hover:text-primary-content absolute top-1/2 right-2 -translate-y-1/2 focus:outline-hidden" | ||
| > | ||
| <PaperAirplaneIcon className="size-6" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| # Get conversation history | ||
| messages = repository.get_messages_by_chatroom_id( | ||
| chatroom_id=item_id, | ||
| limit=200, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| chatroom_id: Mapped[str] = mapped_column( | ||
| ForeignKey("chatrooms.id"), nullable=False, index=True | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chatroom_id 컬럼에 index=True가 설정되어 있지만, __table_args__에서 ("ix_messages_chatroom_created", "chatroom_id", "created_at") 복합 인덱스의 첫 번째 컬럼으로도 사용되고 있습니다. 이는 중복된 인덱스 생성으로 이어질 수 있습니다. 데이터베이스는 보통 복합 인덱스의 첫 번째 컬럼을 단일 컬럼 쿼리에도 효율적으로 사용할 수 있습니다. GENERAL_RULES에 따라 중복을 피하기 위해 index=True를 제거하는 것이 좋습니다.
| chatroom_id: Mapped[str] = mapped_column( | |
| ForeignKey("chatrooms.id"), nullable=False, index=True | |
| ) | |
| chatroom_id: Mapped[str] = mapped_column( | |
| ForeignKey("chatrooms.id"), nullable=False | |
| ) |
References
- When defining SQLAlchemy models, if an index is explicitly created using
Index()in__table_args__, do not also setindex=Trueon the column definition to avoid redundancy.
| openai_api_key=self._settings.api_key, # type: ignore[arg-type] | ||
| openai_organization=self._settings.organization, # type: ignore[arg-type] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
openai_api_key와 openai_organization에 type: ignore[arg-type] 주석이 사용되었습니다. langchain-openai의 ChatOpenAI는 SecretStr 타입을 지원하므로 이 주석은 불필요해 보입니다. 코드 가독성과 유지보수성을 위해 불필요한 type: ignore는 제거하는 것이 좋습니다. 만약 실제로 타입 오류가 발생한다면, 무시하기보다는 명시적인 타입 변환 등 근본적인 해결책을 찾는 것이 바람직합니다.
| openai_api_key=self._settings.api_key, # type: ignore[arg-type] | |
| openai_organization=self._settings.organization, # type: ignore[arg-type] | |
| openai_api_key=self._settings.api_key, | |
| openai_organization=self._settings.organization, |
| }, | ||
| )} | ||
| > | ||
| <div className="max-w-[60%] break-words text-base"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
text-base 클래스는 Tailwind CSS의 기본 유틸리티로, 프로젝트 스타일 가이드(77번 라인)에 따라 사용이 금지되어 있습니다. 대신 tailwind.config.ts에 정의된 MD3 타입 스케일(text-body-m 등)을 사용해야 합니다.
| <div className="max-w-[60%] break-words text-base"> | |
| <div className="max-w-[60%] break-words text-body-m"> |
References
- Tailwind의 기본 타이포그래피 유틸리티(
text-base,text-lg등) 사용을 금지하고, MD3 타입 스케일에 정의된 명명된 유틸리티(text-body-m등)를 사용해야 합니다. (link)
- Add providers/ pattern (LLMProvider Protocol) - Add services/ pattern (ResponseGenerationService) - Reference chatroom domain implementation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Buppy에서 추출한 chatroom 도메인 참조 구현을 추가합니다.
목적
aidol standalone 패키지에 채팅 기능의 참조 구현을 제공하여:
주요 추가 사항
🔧 Backend
🎨 Frontend
📦 의존성
Backend: langchain-core, langchain-openai, litellm
Frontend: react-markdown, remark-breaks, remark-gfm
구조 변경
Backend
Frontend
테스트 체크리스트
참고
~/buppy/backend/aidol_internal/,~/buppy/frontend/src/lib/aidol/