py-aidol 0.3.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {py_aidol-0.3.0 → py_aidol-0.5.0}/PKG-INFO +26 -6
  2. {py_aidol-0.3.0 → py_aidol-0.5.0}/README.md +22 -5
  3. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/api/__init__.py +3 -0
  4. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/api/aidol.py +7 -6
  5. py_aidol-0.5.0/aidol/api/chatroom.py +325 -0
  6. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/api/common.py +4 -3
  7. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/api/companion.py +23 -16
  8. py_aidol-0.5.0/aidol/context/__init__.py +26 -0
  9. py_aidol-0.5.0/aidol/context/builder.py +376 -0
  10. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/factories.py +8 -0
  11. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/models/__init__.py +2 -1
  12. py_aidol-0.5.0/aidol/models/chatroom.py +48 -0
  13. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/protocols.py +64 -0
  14. py_aidol-0.5.0/aidol/providers/__init__.py +9 -0
  15. py_aidol-0.5.0/aidol/providers/llm/__init__.py +15 -0
  16. py_aidol-0.5.0/aidol/providers/llm/base.py +147 -0
  17. py_aidol-0.5.0/aidol/providers/llm/openai.py +101 -0
  18. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/repositories/__init__.py +2 -0
  19. py_aidol-0.5.0/aidol/repositories/chatroom.py +142 -0
  20. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/schemas/__init__.py +35 -0
  21. py_aidol-0.5.0/aidol/schemas/chatroom.py +147 -0
  22. py_aidol-0.5.0/aidol/schemas/model_settings.py +35 -0
  23. py_aidol-0.5.0/aidol/schemas/persona.py +20 -0
  24. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/services/__init__.py +2 -0
  25. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/services/image_generation_service.py +24 -12
  26. py_aidol-0.5.0/aidol/services/response_generation_service.py +63 -0
  27. py_aidol-0.5.0/aidol/settings.py +46 -0
  28. {py_aidol-0.3.0 → py_aidol-0.5.0}/pyproject.toml +4 -1
  29. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/__init__.py +0 -0
  30. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/api/lead.py +0 -0
  31. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/models/aidol.py +0 -0
  32. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/models/aidol_lead.py +0 -0
  33. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/models/companion.py +0 -0
  34. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/py.typed +0 -0
  35. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/repositories/aidol.py +0 -0
  36. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/repositories/aidol_lead.py +0 -0
  37. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/repositories/companion.py +0 -0
  38. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/schemas/aidol.py +0 -0
  39. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/schemas/aidol_lead.py +0 -0
  40. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/schemas/companion.py +0 -0
  41. {py_aidol-0.3.0 → py_aidol-0.5.0}/aidol/services/companion_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-aidol
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: Create and chat with your own AI idol group
5
5
  License: Apache-2.0
6
6
  Keywords: kpop,idol,aidol,ai-companion,chatbot,image-generation
@@ -16,6 +16,9 @@ Requires-Dist: aioia-core (>=2.2.0,<3.0.0)
16
16
  Requires-Dist: fastapi (>=0.115.12,<0.116.0)
17
17
  Requires-Dist: google-genai (>=1.60.0,<2.0.0)
18
18
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
19
+ Requires-Dist: langchain-core (>=0.3.0)
20
+ Requires-Dist: langchain-openai (>=0.3.0)
21
+ Requires-Dist: litellm (>=1.60.0)
19
22
  Requires-Dist: openai (>=1.0.0)
20
23
  Requires-Dist: pillow (>=10.0.0,<11.0.0)
21
24
  Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
@@ -34,7 +37,7 @@ AI 아이돌 그룹 생성 및 채팅 Python 패키지
34
37
  ## 주요 기능
35
38
 
36
39
  - AI 아이돌 그룹/멤버 CRUD
37
- - DALL-E 3 이미지 생성 (엠블럼, 프로필)
40
+ - Google Gemini 이미지 생성 (엠블럼, 프로필)
38
41
  - 텍스트 채팅 (페르소나 기반 응답)
39
42
  - Buppy 통합 Adapter 패턴
40
43
 
@@ -62,7 +65,7 @@ from aidol.factories import AIdolRepositoryFactory, CompanionRepositoryFactory
62
65
  # AIdol 라우터
63
66
  aidol_router = AIdolRouter(
64
67
  repository_factory=AIdolRepositoryFactory(),
65
- openai_settings=openai_settings,
68
+ google_settings=google_settings,
66
69
  image_storage=image_storage,
67
70
  )
68
71
 
@@ -87,11 +90,27 @@ make format
87
90
 
88
91
  ## 환경 변수
89
92
 
90
- ### 필수 (이미지 생성 )
93
+ ### 이미지 생성 인증 (선택, ADC 지원)
91
94
 
92
95
  | 변수 | 설명 |
93
96
  |------|------|
94
- | `OPENAI_API_KEY` | OpenAI API 키 |
97
+ | `GOOGLE_API_KEY` | Google API 키 (Google AI API) |
98
+ | `GOOGLE_CLOUD_PROJECT` | GCP 프로젝트 ID (Vertex AI) |
99
+
100
+ **인증 방법:**
101
+
102
+ **Option 1: Google AI API (API Key)**
103
+ ```bash
104
+ export GOOGLE_API_KEY=your-api-key
105
+ ```
106
+
107
+ **Option 2: Vertex AI (ADC)**
108
+ ```bash
109
+ export GOOGLE_CLOUD_PROJECT=your-project-id
110
+ gcloud auth application-default login # 로컬 개발
111
+ ```
112
+
113
+ > **참고**: Vertex AI 사용 시 `location=global`이 하드코딩되어 있습니다 (Gemini 이미지 생성 모델 요구사항).
95
114
 
96
115
  ### 선택
97
116
 
@@ -106,7 +125,8 @@ make format
106
125
 
107
126
  - aioia-core (공통 인프라)
108
127
  - FastAPI, SQLAlchemy, Pydantic
109
- - OpenAI (이미지 생성, 채팅)
128
+ - Google Generative AI (이미지 생성)
129
+ - OpenAI (채팅)
110
130
  - Pillow (이미지 처리)
111
131
 
112
132
  ## 라이선스
@@ -5,7 +5,7 @@ AI 아이돌 그룹 생성 및 채팅 Python 패키지
5
5
  ## 주요 기능
6
6
 
7
7
  - AI 아이돌 그룹/멤버 CRUD
8
- - DALL-E 3 이미지 생성 (엠블럼, 프로필)
8
+ - Google Gemini 이미지 생성 (엠블럼, 프로필)
9
9
  - 텍스트 채팅 (페르소나 기반 응답)
10
10
  - Buppy 통합 Adapter 패턴
11
11
 
@@ -33,7 +33,7 @@ from aidol.factories import AIdolRepositoryFactory, CompanionRepositoryFactory
33
33
  # AIdol 라우터
34
34
  aidol_router = AIdolRouter(
35
35
  repository_factory=AIdolRepositoryFactory(),
36
- openai_settings=openai_settings,
36
+ google_settings=google_settings,
37
37
  image_storage=image_storage,
38
38
  )
39
39
 
@@ -58,11 +58,27 @@ make format
58
58
 
59
59
  ## 환경 변수
60
60
 
61
- ### 필수 (이미지 생성 )
61
+ ### 이미지 생성 인증 (선택, ADC 지원)
62
62
 
63
63
  | 변수 | 설명 |
64
64
  |------|------|
65
- | `OPENAI_API_KEY` | OpenAI API 키 |
65
+ | `GOOGLE_API_KEY` | Google API 키 (Google AI API) |
66
+ | `GOOGLE_CLOUD_PROJECT` | GCP 프로젝트 ID (Vertex AI) |
67
+
68
+ **인증 방법:**
69
+
70
+ **Option 1: Google AI API (API Key)**
71
+ ```bash
72
+ export GOOGLE_API_KEY=your-api-key
73
+ ```
74
+
75
+ **Option 2: Vertex AI (ADC)**
76
+ ```bash
77
+ export GOOGLE_CLOUD_PROJECT=your-project-id
78
+ gcloud auth application-default login # 로컬 개발
79
+ ```
80
+
81
+ > **참고**: Vertex AI 사용 시 `location=global`이 하드코딩되어 있습니다 (Gemini 이미지 생성 모델 요구사항).
66
82
 
67
83
  ### 선택
68
84
 
@@ -77,7 +93,8 @@ make format
77
93
 
78
94
  - aioia-core (공통 인프라)
79
95
  - FastAPI, SQLAlchemy, Pydantic
80
- - OpenAI (이미지 생성, 채팅)
96
+ - Google Generative AI (이미지 생성)
97
+ - OpenAI (채팅)
81
98
  - Pillow (이미지 처리)
82
99
 
83
100
  ## 라이선스
@@ -3,11 +3,14 @@ AIdol API routers
3
3
  """
4
4
 
5
5
  from aidol.api.aidol import AIdolRouter, create_aidol_router
6
+ from aidol.api.chatroom import ChatroomRouter, create_chatroom_router
6
7
  from aidol.api.companion import CompanionRouter, create_companion_router
7
8
 
8
9
  __all__ = [
9
10
  "AIdolRouter",
11
+ "ChatroomRouter",
10
12
  "CompanionRouter",
11
13
  "create_aidol_router",
14
+ "create_chatroom_router",
12
15
  "create_companion_router",
13
16
  ]
@@ -21,6 +21,7 @@ from aidol.protocols import (
21
21
  ImageStorageProtocol,
22
22
  )
23
23
  from aidol.schemas import AIdol, AIdolCreate, AIdolPublic, AIdolUpdate
24
+ from aidol.settings import GoogleGenAISettings
24
25
 
25
26
 
26
27
  class AIdolCreateResponse(BaseModel):
@@ -41,11 +42,11 @@ class AIdolRouter(
41
42
 
42
43
  def __init__(
43
44
  self,
44
- google_api_key: str | None,
45
+ google_settings: GoogleGenAISettings | None,
45
46
  image_storage: ImageStorageProtocol,
46
47
  **kwargs,
47
48
  ):
48
- self.google_api_key = google_api_key
49
+ self.google_settings = google_settings
49
50
  self.image_storage = image_storage
50
51
  super().__init__(**kwargs)
51
52
 
@@ -55,7 +56,7 @@ class AIdolRouter(
55
56
  register_image_generation_route(
56
57
  router=self.router,
57
58
  resource_name=self.resource_name,
58
- google_api_key=self.google_api_key,
59
+ google_settings=self.google_settings,
59
60
  image_storage=self.image_storage,
60
61
  )
61
62
 
@@ -142,7 +143,7 @@ class AIdolRouter(
142
143
 
143
144
 
144
145
  def create_aidol_router(
145
- google_api_key: str | None,
146
+ google_settings: GoogleGenAISettings | None,
146
147
  db_session_factory: sessionmaker,
147
148
  repository_factory: AIdolRepositoryFactoryProtocol,
148
149
  image_storage: ImageStorageProtocol,
@@ -155,7 +156,7 @@ def create_aidol_router(
155
156
  Create AIdol router with dependency injection.
156
157
 
157
158
  Args:
158
- google_api_key: Google API Key for image generation
159
+ google_settings: Google API settings (uses ADC if api_key is None)
159
160
  db_session_factory: Database session factory
160
161
  repository_factory: Factory implementing AIdolRepositoryFactoryProtocol
161
162
  image_storage: Image storage for permanent URLs
@@ -168,7 +169,7 @@ def create_aidol_router(
168
169
  FastAPI APIRouter instance
169
170
  """
170
171
  router = AIdolRouter(
171
- google_api_key=google_api_key,
172
+ google_settings=google_settings,
172
173
  image_storage=image_storage,
173
174
  model_class=AIdol,
174
175
  create_schema=AIdolCreate,
@@ -0,0 +1,325 @@
1
+ """
2
+ Chatroom API router
3
+
4
+ Implements BaseCrudRouter pattern for consistency with aioia-core patterns.
5
+ """
6
+
7
+ from aioia_core.auth import UserInfoProvider
8
+ from aioia_core.errors import ErrorResponse
9
+ from aioia_core.fastapi import BaseCrudRouter
10
+ from aioia_core.settings import JWTSettings, OpenAIAPISettings
11
+ from fastapi import APIRouter, Depends, HTTPException, status
12
+ from humps import camelize
13
+ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
14
+ from pydantic import BaseModel, ConfigDict, Field
15
+ from sqlalchemy.orm import Session, sessionmaker
16
+
17
+ from aidol.context import MessageContextBuilder
18
+ from aidol.protocols import (
19
+ ChatroomRepositoryFactoryProtocol,
20
+ ChatroomRepositoryProtocol,
21
+ CompanionRepositoryFactoryProtocol,
22
+ )
23
+ from aidol.providers.llm import OpenAILLMProvider
24
+ from aidol.schemas import (
25
+ Chatroom,
26
+ ChatroomCreate,
27
+ ChatroomUpdate,
28
+ CompanionMessageCreate,
29
+ Message,
30
+ MessageCreate,
31
+ ModelSettings,
32
+ Persona,
33
+ SenderType,
34
+ )
35
+ from aidol.services import ResponseGenerationService
36
+ from aidol.settings import Settings
37
+
38
+ # Maximum number of messages to fetch for conversation history
39
+ DEFAULT_HISTORY_LIMIT = 200
40
+
41
+
42
+ class ChatroomSingleItemResponse(BaseModel):
43
+ """Single item response for chatroom."""
44
+
45
+ data: Chatroom
46
+
47
+
48
+ class GenerateResponse(BaseModel):
49
+ """Response schema for generate_response endpoint."""
50
+
51
+ model_config = ConfigDict(populate_by_name=True, alias_generator=camelize)
52
+
53
+ message_id: str = Field(..., description="Message ID")
54
+ content: str = Field(..., description="AI response content")
55
+
56
+
57
+ def _to_langchain_messages(messages: list[Message]) -> list[BaseMessage]:
58
+ """
59
+ Convert Message schemas to LangChain BaseMessage format.
60
+
61
+ Args:
62
+ messages: List of Message from repository.
63
+
64
+ Returns:
65
+ List of LangChain BaseMessage (HumanMessage or AIMessage).
66
+ """
67
+ result: list[BaseMessage] = []
68
+ for msg in messages:
69
+ if msg.sender_type == SenderType.USER:
70
+ result.append(HumanMessage(content=msg.content))
71
+ else:
72
+ result.append(AIMessage(content=msg.content))
73
+ return result
74
+
75
+
76
+ class ChatroomRouter(
77
+ BaseCrudRouter[Chatroom, ChatroomCreate, ChatroomUpdate, ChatroomRepositoryProtocol]
78
+ ):
79
+ """
80
+ Chatroom router with custom message endpoints.
81
+
82
+ Extends BaseCrudRouter for consistent architecture pattern.
83
+ Disables default CRUD endpoints and provides custom message endpoints.
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ model_settings: Settings,
89
+ openai_settings: OpenAIAPISettings,
90
+ companion_repository_factory: CompanionRepositoryFactoryProtocol,
91
+ **kwargs,
92
+ ):
93
+ super().__init__(**kwargs)
94
+ self.model_settings = model_settings
95
+ self.openai_settings = openai_settings
96
+ self.companion_repository_factory = companion_repository_factory
97
+
98
+ def _register_routes(self) -> None:
99
+ """Register routes (fancall pattern: public CRUD + message endpoints)"""
100
+ # Chatroom CRUD (public, no auth)
101
+ self._register_public_create_route()
102
+ self._register_public_get_route()
103
+
104
+ # Message endpoints (public)
105
+ self._register_get_messages_route()
106
+ self._register_send_message_route()
107
+ self._register_generate_response_route()
108
+
109
+ def _register_public_create_route(self) -> None:
110
+ """POST /{resource_name} - Create a chatroom (public, fancall pattern)"""
111
+
112
+ @self.router.post(
113
+ f"/{self.resource_name}",
114
+ response_model=ChatroomSingleItemResponse,
115
+ status_code=status.HTTP_201_CREATED,
116
+ summary="Create chatroom",
117
+ description="Create a new chatroom (public endpoint)",
118
+ )
119
+ async def create_chatroom(
120
+ request: ChatroomCreate,
121
+ repository: ChatroomRepositoryProtocol = Depends(self.get_repository_dep),
122
+ ):
123
+ """Create a new chatroom."""
124
+ created = repository.create(request)
125
+ return ChatroomSingleItemResponse(data=created)
126
+
127
+ def _register_public_get_route(self) -> None:
128
+ """GET /{resource_name}/{id} - Get a chatroom (public, fancall pattern)"""
129
+
130
+ @self.router.get(
131
+ f"/{self.resource_name}/{{item_id}}",
132
+ response_model=ChatroomSingleItemResponse,
133
+ status_code=status.HTTP_200_OK,
134
+ summary="Get chatroom",
135
+ description="Get chatroom by ID (public endpoint)",
136
+ responses={
137
+ 404: {"model": ErrorResponse, "description": "Chatroom not found"},
138
+ },
139
+ )
140
+ async def get_chatroom(
141
+ item_id: str,
142
+ repository: ChatroomRepositoryProtocol = Depends(self.get_repository_dep),
143
+ ):
144
+ """Get chatroom by ID."""
145
+ chatroom = self._get_item_or_404(repository, item_id)
146
+ return ChatroomSingleItemResponse(data=chatroom)
147
+
148
+ def _register_get_messages_route(self) -> None:
149
+ """GET /{resource_name}/{id}/messages - Get messages from a chatroom"""
150
+
151
+ @self.router.get(
152
+ f"/{self.resource_name}/{{item_id}}/messages",
153
+ response_model=list[Message],
154
+ status_code=status.HTTP_200_OK,
155
+ summary="Get messages",
156
+ description="Get messages from a chatroom",
157
+ )
158
+ async def get_messages(
159
+ item_id: str,
160
+ limit: int = 100,
161
+ offset: int = 0,
162
+ repository: ChatroomRepositoryProtocol = Depends(self.get_repository_dep),
163
+ ):
164
+ """Get messages from a chatroom."""
165
+ return repository.get_messages_by_chatroom_id(
166
+ chatroom_id=item_id,
167
+ limit=limit,
168
+ offset=offset,
169
+ )
170
+
171
+ def _register_send_message_route(self) -> None:
172
+ """POST /{resource_name}/{id}/messages - Send a message to a chatroom"""
173
+
174
+ @self.router.post(
175
+ f"/{self.resource_name}/{{item_id}}/messages",
176
+ response_model=Message,
177
+ status_code=status.HTTP_201_CREATED,
178
+ summary="Send message",
179
+ description="Send a message to a chatroom",
180
+ )
181
+ async def send_message(
182
+ item_id: str,
183
+ request: MessageCreate,
184
+ repository: ChatroomRepositoryProtocol = Depends(self.get_repository_dep),
185
+ ):
186
+ """Send a message to a chatroom."""
187
+ # Verify chatroom exists
188
+ self._get_item_or_404(repository, item_id)
189
+
190
+ # Enforce sender_type as USER to prevent spoofing
191
+ request.sender_type = SenderType.USER
192
+
193
+ # Pass MessageCreate directly (aioia-core pattern)
194
+ return repository.add_message_to_chatroom(
195
+ chatroom_id=item_id,
196
+ message=request,
197
+ )
198
+
199
+ def _register_generate_response_route(self) -> None:
200
+ """POST /{resource_name}/{id}/companions/{companion_id}/response - Generate AI response"""
201
+
202
+ @self.router.post(
203
+ f"/{self.resource_name}/{{item_id}}/companions/{{companion_id}}/response",
204
+ response_model=GenerateResponse,
205
+ status_code=status.HTTP_201_CREATED,
206
+ summary="Generate AI response",
207
+ description="Generate AI response for a chatroom with a specific companion",
208
+ )
209
+ async def generate_response(
210
+ item_id: str,
211
+ companion_id: str,
212
+ db_session: Session = Depends(self.get_db_dep),
213
+ repository: ChatroomRepositoryProtocol = Depends(self.get_repository_dep),
214
+ ):
215
+ """Generate AI response for a chatroom."""
216
+ # Verify chatroom exists
217
+ self._get_item_or_404(repository, item_id)
218
+
219
+ # Get companion repository with same db session (Buppy pattern)
220
+ companion_repository = self.companion_repository_factory.create_repository(
221
+ db_session
222
+ )
223
+
224
+ # Get companion by ID
225
+ companion = companion_repository.get_by_id(companion_id)
226
+ if companion is None:
227
+ raise HTTPException(
228
+ status_code=status.HTTP_404_NOT_FOUND,
229
+ detail=f"Companion with id {companion_id} not found",
230
+ )
231
+
232
+ # Get conversation history
233
+ messages = repository.get_messages_by_chatroom_id(
234
+ chatroom_id=item_id,
235
+ limit=DEFAULT_HISTORY_LIMIT,
236
+ offset=0,
237
+ )
238
+
239
+ # Convert to LangChain BaseMessage format
240
+ # Reverse: DB returns newest-first, LLM needs chronological order
241
+ langchain_messages = _to_langchain_messages(list(reversed(messages)))
242
+
243
+ # Create persona from companion (KST fixed for MVP)
244
+ persona = Persona(
245
+ name=companion.name,
246
+ system_prompt=companion.system_prompt,
247
+ timezone_name="Asia/Seoul",
248
+ )
249
+ provider = OpenAILLMProvider(settings=self.openai_settings)
250
+ model_settings = ModelSettings(chat_model=self.model_settings.openai_model)
251
+
252
+ # Generate text response using ResponseGenerationService
253
+ context = (
254
+ MessageContextBuilder(provider, persona)
255
+ .with_persona()
256
+ .with_real_time_context()
257
+ .with_current_conversation(langchain_messages)
258
+ .build()
259
+ )
260
+ service = ResponseGenerationService(provider, model_settings)
261
+ response_text = service.generate_response(context)
262
+
263
+ # Save companion message (repository handles commit)
264
+ # Use CompanionMessageCreate (no id) - aioia-core pattern
265
+ companion_message = repository.add_message_to_chatroom(
266
+ chatroom_id=item_id,
267
+ message=CompanionMessageCreate(
268
+ content=response_text,
269
+ companion_id=companion_id,
270
+ ),
271
+ )
272
+
273
+ return GenerateResponse(
274
+ message_id=companion_message.id,
275
+ content=response_text,
276
+ )
277
+
278
+
279
+ def create_chatroom_router(
280
+ openai_settings: OpenAIAPISettings,
281
+ model_settings: Settings,
282
+ companion_repository_factory: CompanionRepositoryFactoryProtocol,
283
+ db_session_factory: sessionmaker,
284
+ repository_factory: ChatroomRepositoryFactoryProtocol,
285
+ jwt_settings: JWTSettings | None = None,
286
+ user_info_provider: UserInfoProvider | None = None,
287
+ resource_name: str = "chatrooms",
288
+ tags: list[str] | None = None,
289
+ ) -> APIRouter:
290
+ """
291
+ Create chatroom router with dependency injection.
292
+
293
+ Args:
294
+ openai_settings: OpenAI API settings for LLM provider
295
+ model_settings: Environment settings for aidol
296
+ companion_repository_factory: Factory for CompanionRepository.
297
+ For standalone: Use aidol.factories.CompanionRepositoryFactory.
298
+ For platform integration: Use CompanionRepositoryFactoryAdapter.
299
+ db_session_factory: Database session factory
300
+ repository_factory: Factory implementing ChatroomRepositoryFactoryProtocol.
301
+ For standalone: Use aidol.factories.ChatroomRepositoryFactory.
302
+ For platform integration: Use ChatroomRepositoryFactoryAdapter.
303
+ jwt_settings: Optional JWT settings for authentication
304
+ user_info_provider: Optional user info provider
305
+ resource_name: Resource name for routes (default: "chatrooms")
306
+ tags: Optional OpenAPI tags
307
+
308
+ Returns:
309
+ FastAPI APIRouter instance
310
+ """
311
+ router = ChatroomRouter(
312
+ model_settings=model_settings,
313
+ openai_settings=openai_settings,
314
+ companion_repository_factory=companion_repository_factory,
315
+ model_class=Chatroom,
316
+ create_schema=ChatroomCreate,
317
+ update_schema=ChatroomUpdate,
318
+ db_session_factory=db_session_factory,
319
+ repository_factory=repository_factory,
320
+ user_info_provider=user_info_provider,
321
+ jwt_secret_key=jwt_settings.secret_key if jwt_settings else None,
322
+ resource_name=resource_name,
323
+ tags=tags or ["Aidol"],
324
+ )
325
+ return router.get_router()
@@ -14,12 +14,13 @@ from aidol.schemas import (
14
14
  ImageGenerationResponse,
15
15
  )
16
16
  from aidol.services import ImageGenerationService
17
+ from aidol.settings import GoogleGenAISettings
17
18
 
18
19
 
19
20
  def register_image_generation_route(
20
21
  router: APIRouter,
21
22
  resource_name: str,
22
- google_api_key: str | None,
23
+ google_settings: GoogleGenAISettings | None,
23
24
  image_storage: ImageStorageProtocol,
24
25
  ) -> None:
25
26
  """
@@ -28,7 +29,7 @@ def register_image_generation_route(
28
29
  Args:
29
30
  router: FastAPI APIRouter instance
30
31
  resource_name: Resource name for the route path
31
- google_api_key: Google API Key
32
+ google_settings: Google API settings (API Key or Vertex AI with ADC)
32
33
  image_storage: Image Storage instance
33
34
  """
34
35
 
@@ -45,7 +46,7 @@ def register_image_generation_route(
45
46
  async def generate_image(request: ImageGenerationRequest):
46
47
  """Generate image from prompt."""
47
48
  # Generate and download image
48
- service = ImageGenerationService(api_key=google_api_key)
49
+ service = ImageGenerationService(settings=google_settings)
49
50
  image = service.generate_and_download_image(
50
51
  prompt=request.prompt,
51
52
  size="1024x1024",