py-aidol 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- aidol/api/aidol.py +67 -73
- aidol/api/common.py +71 -0
- aidol/api/companion.py +156 -39
- aidol/api/lead.py +123 -0
- aidol/factories.py +8 -0
- aidol/models/__init__.py +2 -1
- aidol/models/aidol.py +4 -2
- aidol/models/aidol_lead.py +22 -0
- aidol/models/companion.py +20 -2
- aidol/protocols.py +26 -0
- aidol/repositories/__init__.py +2 -0
- aidol/repositories/aidol.py +2 -0
- aidol/repositories/aidol_lead.py +49 -0
- aidol/repositories/companion.py +38 -5
- aidol/schemas/__init__.py +12 -0
- aidol/schemas/aidol.py +10 -6
- aidol/schemas/aidol_lead.py +38 -0
- aidol/schemas/companion.py +122 -5
- aidol/services/companion_service.py +96 -0
- aidol/services/image_generation_service.py +56 -101
- {py_aidol-0.1.0.dist-info → py_aidol-0.3.0.dist-info}/METADATA +2 -1
- py_aidol-0.3.0.dist-info/RECORD +27 -0
- py_aidol-0.1.0.dist-info/RECORD +0 -21
- {py_aidol-0.1.0.dist-info → py_aidol-0.3.0.dist-info}/WHEEL +0 -0
aidol/schemas/companion.py
CHANGED
|
@@ -2,18 +2,77 @@
|
|
|
2
2
|
Companion (member) schemas
|
|
3
3
|
|
|
4
4
|
Schema hierarchy:
|
|
5
|
+
- CompanionStats: Nested stats object for request/response
|
|
5
6
|
- CompanionBase: Mutable fields (used in Create/Update)
|
|
6
7
|
- CompanionCreate: Base + system_prompt (mutable, but sensitive)
|
|
7
|
-
- CompanionUpdate:
|
|
8
|
+
- CompanionUpdate: All fields optional for partial updates
|
|
8
9
|
- Companion: Response with all fields including system_prompt (internal use)
|
|
9
10
|
- CompanionPublic: Response without sensitive fields (API use)
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
13
15
|
|
|
14
16
|
from humps import camelize
|
|
15
17
|
from pydantic import BaseModel, ConfigDict, Field
|
|
16
18
|
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# Enums
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Gender(str, Enum):
|
|
25
|
+
"""Gender options for companions."""
|
|
26
|
+
|
|
27
|
+
MALE = "male"
|
|
28
|
+
FEMALE = "female"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Grade(str, Enum):
|
|
32
|
+
"""Grade levels for companions."""
|
|
33
|
+
|
|
34
|
+
A = "A"
|
|
35
|
+
B = "B"
|
|
36
|
+
C = "C"
|
|
37
|
+
F = "F"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Position(str, Enum):
|
|
41
|
+
"""Position roles in the group."""
|
|
42
|
+
|
|
43
|
+
LEADER = "leader"
|
|
44
|
+
MAIN_VOCAL = "mainVocal"
|
|
45
|
+
SUB_VOCAL = "subVocal"
|
|
46
|
+
MAIN_DANCER = "mainDancer"
|
|
47
|
+
SUB_DANCER = "subDancer"
|
|
48
|
+
MAIN_RAPPER = "mainRapper"
|
|
49
|
+
SUB_RAPPER = "subRapper"
|
|
50
|
+
VISUAL = "visual"
|
|
51
|
+
MAKNAE = "maknae"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Nested Objects
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CompanionStats(BaseModel):
|
|
60
|
+
"""Nested stats object for API request/response."""
|
|
61
|
+
|
|
62
|
+
model_config = ConfigDict(populate_by_name=True, alias_generator=camelize)
|
|
63
|
+
|
|
64
|
+
vocal: int = Field(default=0, ge=0, le=100, description="Vocal skill")
|
|
65
|
+
dance: int = Field(default=0, ge=0, le=100, description="Dance skill")
|
|
66
|
+
rap: int = Field(default=0, ge=0, le=100, description="Rap skill")
|
|
67
|
+
visual: int = Field(default=0, ge=0, le=100, description="Visual score")
|
|
68
|
+
stamina: int = Field(default=0, ge=0, le=100, description="Stamina")
|
|
69
|
+
charm: int = Field(default=0, ge=0, le=100, description="Charm score")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# Request Schemas
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
|
|
17
76
|
|
|
18
77
|
class CompanionBase(BaseModel):
|
|
19
78
|
"""Base companion model with common mutable fields.
|
|
@@ -25,11 +84,31 @@ class CompanionBase(BaseModel):
|
|
|
25
84
|
model_config = ConfigDict(populate_by_name=True, alias_generator=camelize)
|
|
26
85
|
|
|
27
86
|
aidol_id: str | None = Field(default=None, description="AIdol group ID")
|
|
28
|
-
name: str = Field(
|
|
87
|
+
name: str | None = Field(default=None, description="Companion name")
|
|
88
|
+
gender: Gender | None = Field(default=None, description="Gender")
|
|
89
|
+
grade: Grade | None = Field(default=None, description="Grade level")
|
|
29
90
|
biography: str | None = Field(default=None, description="Companion biography")
|
|
30
91
|
profile_picture_url: str | None = Field(
|
|
31
92
|
default=None, description="Profile picture URL"
|
|
32
93
|
)
|
|
94
|
+
position: Position | None = Field(default=None, description="Position in group")
|
|
95
|
+
|
|
96
|
+
# MBTI scores (1-10)
|
|
97
|
+
mbti_energy: int | None = Field(default=None, ge=1, le=10, description="E↔I (1-10)")
|
|
98
|
+
mbti_perception: int | None = Field(
|
|
99
|
+
default=None, ge=1, le=10, description="S↔N (1-10)"
|
|
100
|
+
)
|
|
101
|
+
mbti_judgment: int | None = Field(
|
|
102
|
+
default=None, ge=1, le=10, description="T↔F (1-10)"
|
|
103
|
+
)
|
|
104
|
+
mbti_lifestyle: int | None = Field(
|
|
105
|
+
default=None, ge=1, le=10, description="J↔P (1-10)"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Stats (nested object)
|
|
109
|
+
stats: CompanionStats = Field(
|
|
110
|
+
default_factory=CompanionStats, description="Ability stats"
|
|
111
|
+
)
|
|
33
112
|
|
|
34
113
|
|
|
35
114
|
class CompanionCreate(CompanionBase):
|
|
@@ -50,14 +129,37 @@ class CompanionUpdate(BaseModel):
|
|
|
50
129
|
|
|
51
130
|
aidol_id: str | None = Field(default=None, description="AIdol group ID")
|
|
52
131
|
name: str | None = Field(default=None, description="Companion name")
|
|
132
|
+
gender: Gender | None = Field(default=None, description="Gender")
|
|
133
|
+
grade: Grade | None = Field(default=None, description="Grade level")
|
|
53
134
|
biography: str | None = Field(default=None, description="Companion biography")
|
|
54
135
|
profile_picture_url: str | None = Field(
|
|
55
136
|
default=None, description="Profile picture URL"
|
|
56
137
|
)
|
|
138
|
+
position: Position | None = Field(default=None, description="Position in group")
|
|
57
139
|
system_prompt: str | None = Field(
|
|
58
140
|
default=None, description="AI system prompt (not exposed in responses)"
|
|
59
141
|
)
|
|
60
142
|
|
|
143
|
+
# MBTI scores (1-10)
|
|
144
|
+
mbti_energy: int | None = Field(default=None, ge=1, le=10, description="E↔I (1-10)")
|
|
145
|
+
mbti_perception: int | None = Field(
|
|
146
|
+
default=None, ge=1, le=10, description="S↔N (1-10)"
|
|
147
|
+
)
|
|
148
|
+
mbti_judgment: int | None = Field(
|
|
149
|
+
default=None, ge=1, le=10, description="T↔F (1-10)"
|
|
150
|
+
)
|
|
151
|
+
mbti_lifestyle: int | None = Field(
|
|
152
|
+
default=None, ge=1, le=10, description="J↔P (1-10)"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Stats (nested object, optional for updates)
|
|
156
|
+
stats: CompanionStats | None = Field(default=None, description="Ability stats")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ---------------------------------------------------------------------------
|
|
160
|
+
# Response Schemas
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
|
|
61
163
|
|
|
62
164
|
class Companion(CompanionBase):
|
|
63
165
|
"""Companion response schema with id and timestamps.
|
|
@@ -78,10 +180,12 @@ class Companion(CompanionBase):
|
|
|
78
180
|
updated_at: datetime = Field(..., description="Last update timestamp")
|
|
79
181
|
|
|
80
182
|
|
|
81
|
-
class CompanionPublic(
|
|
82
|
-
"""Public companion response schema
|
|
183
|
+
class CompanionPublic(BaseModel):
|
|
184
|
+
"""Public companion response schema for frontend.
|
|
83
185
|
|
|
84
|
-
Excludes system_prompt for
|
|
186
|
+
- Excludes system_prompt for security
|
|
187
|
+
- Uses nested stats object
|
|
188
|
+
- Includes calculated mbti string
|
|
85
189
|
"""
|
|
86
190
|
|
|
87
191
|
model_config = ConfigDict(
|
|
@@ -89,5 +193,18 @@ class CompanionPublic(CompanionBase):
|
|
|
89
193
|
)
|
|
90
194
|
|
|
91
195
|
id: str = Field(..., description="Companion ID")
|
|
196
|
+
aidol_id: str | None = Field(default=None, description="AIdol group ID")
|
|
197
|
+
name: str | None = Field(default=None, description="Companion name")
|
|
198
|
+
gender: Gender | None = Field(default=None, description="Gender")
|
|
199
|
+
grade: Grade | None = Field(default=None, description="Grade level")
|
|
200
|
+
biography: str | None = Field(default=None, description="Companion biography")
|
|
201
|
+
profile_picture_url: str | None = Field(
|
|
202
|
+
default=None, description="Profile picture URL"
|
|
203
|
+
)
|
|
204
|
+
position: Position | None = Field(default=None, description="Position in group")
|
|
205
|
+
mbti: str | None = Field(default=None, description="Calculated MBTI (e.g., ENFP)")
|
|
206
|
+
stats: CompanionStats = Field(
|
|
207
|
+
default_factory=CompanionStats, description="Ability stats"
|
|
208
|
+
)
|
|
92
209
|
created_at: datetime = Field(..., description="Creation timestamp")
|
|
93
210
|
updated_at: datetime = Field(..., description="Last update timestamp")
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Companion service
|
|
3
|
+
|
|
4
|
+
Business logic for Companion operations including grade and MBTI calculation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from aidol.schemas.companion import Companion, CompanionPublic, CompanionStats, Grade
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def calculate_grade(stats: CompanionStats) -> Grade:
|
|
11
|
+
"""Calculate grade based on stats average.
|
|
12
|
+
|
|
13
|
+
- A: 80-100
|
|
14
|
+
- B: 60-79
|
|
15
|
+
- C: 40-59
|
|
16
|
+
- F: 0-39
|
|
17
|
+
"""
|
|
18
|
+
avg = (
|
|
19
|
+
(stats.vocal or 0)
|
|
20
|
+
+ (stats.dance or 0)
|
|
21
|
+
+ (stats.rap or 0)
|
|
22
|
+
+ (stats.visual or 0)
|
|
23
|
+
+ (stats.stamina or 0)
|
|
24
|
+
+ (stats.charm or 0)
|
|
25
|
+
) / 6
|
|
26
|
+
if avg >= 80:
|
|
27
|
+
return Grade.A
|
|
28
|
+
if avg >= 60:
|
|
29
|
+
return Grade.B
|
|
30
|
+
if avg >= 40:
|
|
31
|
+
return Grade.C
|
|
32
|
+
return Grade.F
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def calculate_mbti(
|
|
36
|
+
energy: int | None,
|
|
37
|
+
perception: int | None,
|
|
38
|
+
judgment: int | None,
|
|
39
|
+
lifestyle: int | None,
|
|
40
|
+
) -> str | None:
|
|
41
|
+
"""Calculate MBTI string from 4 dimension scores.
|
|
42
|
+
|
|
43
|
+
Each score is 1-10:
|
|
44
|
+
- energy: 1-5 = E, 6-10 = I
|
|
45
|
+
- perception: 1-5 = N, 6-10 = S
|
|
46
|
+
- judgment: 1-5 = T, 6-10 = F
|
|
47
|
+
- lifestyle: 1-5 = P, 6-10 = J
|
|
48
|
+
|
|
49
|
+
Returns None if any dimension is missing.
|
|
50
|
+
"""
|
|
51
|
+
if any(v is None for v in (energy, perception, judgment, lifestyle)):
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
assert energy is not None
|
|
55
|
+
assert perception is not None
|
|
56
|
+
assert judgment is not None
|
|
57
|
+
assert lifestyle is not None
|
|
58
|
+
|
|
59
|
+
e_i = "E" if energy <= 5 else "I"
|
|
60
|
+
n_s = "N" if perception <= 5 else "S"
|
|
61
|
+
t_f = "T" if judgment <= 5 else "F"
|
|
62
|
+
p_j = "P" if lifestyle <= 5 else "J"
|
|
63
|
+
|
|
64
|
+
return f"{e_i}{n_s}{t_f}{p_j}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def to_companion_public(companion: Companion) -> CompanionPublic:
|
|
68
|
+
"""Convert Companion to CompanionPublic with calculated grade and mbti."""
|
|
69
|
+
# Build stats object
|
|
70
|
+
stats = companion.stats if companion.stats else CompanionStats()
|
|
71
|
+
|
|
72
|
+
# Calculate grade from stats
|
|
73
|
+
grade = calculate_grade(stats)
|
|
74
|
+
|
|
75
|
+
# Calculate MBTI from 4 dimensions
|
|
76
|
+
mbti = calculate_mbti(
|
|
77
|
+
companion.mbti_energy,
|
|
78
|
+
companion.mbti_perception,
|
|
79
|
+
companion.mbti_judgment,
|
|
80
|
+
companion.mbti_lifestyle,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return CompanionPublic(
|
|
84
|
+
id=companion.id,
|
|
85
|
+
aidol_id=companion.aidol_id,
|
|
86
|
+
name=companion.name,
|
|
87
|
+
gender=companion.gender,
|
|
88
|
+
grade=grade,
|
|
89
|
+
biography=companion.biography,
|
|
90
|
+
profile_picture_url=companion.profile_picture_url,
|
|
91
|
+
position=companion.position,
|
|
92
|
+
mbti=mbti,
|
|
93
|
+
stats=stats,
|
|
94
|
+
created_at=companion.created_at,
|
|
95
|
+
updated_at=companion.updated_at,
|
|
96
|
+
)
|
|
@@ -4,142 +4,97 @@ Image generation service for AIdol
|
|
|
4
4
|
Generates images using OpenAI DALL-E 3 for AIdol emblems and Companion profiles.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
7
9
|
import logging
|
|
8
10
|
from dataclasses import dataclass
|
|
9
11
|
from io import BytesIO
|
|
10
12
|
from typing import Literal
|
|
11
13
|
|
|
12
|
-
import httpx
|
|
13
|
-
import openai
|
|
14
14
|
import PIL.Image
|
|
15
|
-
from
|
|
15
|
+
from google import genai
|
|
16
|
+
from google.genai import errors as genai_errors
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
@dataclass
|
|
21
22
|
class ImageGenerationResponse:
|
|
22
|
-
"""Structured response
|
|
23
|
+
"""Structured response for compatibility (legacy)"""
|
|
23
24
|
|
|
24
|
-
url: str
|
|
25
|
-
revised_prompt: str | None
|
|
25
|
+
url: str | None = None
|
|
26
|
+
revised_prompt: str | None = None
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class ImageGenerationService:
|
|
29
|
-
"""Service for generating images using
|
|
30
|
+
"""Service for generating images using Google Gemini 3 (Imagen)."""
|
|
31
|
+
|
|
32
|
+
client: "genai.Client | None" = None
|
|
30
33
|
|
|
31
|
-
def __init__(self,
|
|
34
|
+
def __init__(self, api_key: str | None = None, settings=None):
|
|
32
35
|
"""
|
|
33
|
-
Initialize the Image Generation service
|
|
36
|
+
Initialize the Image Generation service.
|
|
34
37
|
|
|
35
38
|
Args:
|
|
36
|
-
|
|
39
|
+
api_key: Google API Key.
|
|
40
|
+
settings: Unused, kept for compatibility.
|
|
37
41
|
"""
|
|
38
|
-
self.settings = openai_settings
|
|
39
|
-
self.client = openai.OpenAI(api_key=self.settings.api_key)
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
# Use explicitly provided api_key, otherwise fallback to settings or env
|
|
44
|
+
if api_key:
|
|
45
|
+
self.client = genai.Client(api_key=api_key)
|
|
46
|
+
elif settings and hasattr(settings, "api_key") and settings.api_key:
|
|
47
|
+
self.client = genai.Client(api_key=settings.api_key)
|
|
48
|
+
else:
|
|
49
|
+
# Try loading from GOOGLE_API_KEY environment variable (Client handles this)
|
|
50
|
+
self.client = genai.Client()
|
|
51
|
+
|
|
52
|
+
def generate_and_download_image(
|
|
42
53
|
self,
|
|
43
54
|
prompt: str,
|
|
44
|
-
size: Literal[
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"1024x1792",
|
|
48
|
-
] = "1024x1024",
|
|
49
|
-
quality: Literal["standard", "hd"] = "standard",
|
|
50
|
-
) -> ImageGenerationResponse | None:
|
|
55
|
+
size: Literal["1024x1024"] = "1024x1024", # pylint: disable=unused-argument
|
|
56
|
+
quality: Literal["standard"] = "standard", # pylint: disable=unused-argument
|
|
57
|
+
) -> PIL.Image.Image | None:
|
|
51
58
|
"""
|
|
52
|
-
Generate an image
|
|
59
|
+
Generate an image using Gemini 3 and return as PIL Image.
|
|
53
60
|
|
|
54
61
|
Args:
|
|
55
|
-
prompt: Text description
|
|
56
|
-
size:
|
|
57
|
-
quality:
|
|
62
|
+
prompt: Text description.
|
|
63
|
+
size: Ignored (Gemini specific).
|
|
64
|
+
quality: Ignored (Gemini specific).
|
|
58
65
|
|
|
59
66
|
Returns:
|
|
60
|
-
|
|
61
|
-
or None if generation fails.
|
|
62
|
-
|
|
63
|
-
Raises:
|
|
64
|
-
openai.OpenAIError: If OpenAI API call fails.
|
|
67
|
+
PIL Image object, or None if generation fails.
|
|
65
68
|
"""
|
|
66
|
-
|
|
67
|
-
logger.
|
|
68
|
-
|
|
69
|
-
model="dall-e-3",
|
|
70
|
-
prompt=prompt,
|
|
71
|
-
size=size,
|
|
72
|
-
quality=quality,
|
|
73
|
-
n=1,
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
if not response.data or len(response.data) == 0:
|
|
77
|
-
logger.error("No image data returned from OpenAI")
|
|
78
|
-
return None
|
|
79
|
-
|
|
80
|
-
image_data = response.data[0]
|
|
81
|
-
url = image_data.url
|
|
82
|
-
|
|
83
|
-
if not url:
|
|
84
|
-
logger.error("No URL found in image response")
|
|
85
|
-
return None
|
|
69
|
+
if not self.client:
|
|
70
|
+
logger.error("Gemini client not initialized")
|
|
71
|
+
return None
|
|
86
72
|
|
|
87
|
-
|
|
73
|
+
try:
|
|
74
|
+
logger.info("Generating image with Gemini 3 (prompt: %s)...", prompt[:50])
|
|
88
75
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
revised_prompt=revised_prompt,
|
|
76
|
+
response = self.client.models.generate_content(
|
|
77
|
+
model="gemini-3-pro-image-preview",
|
|
78
|
+
contents=[prompt], # type: ignore[arg-type]
|
|
93
79
|
)
|
|
94
80
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
# Iterate parts to find the image
|
|
82
|
+
if response.parts:
|
|
83
|
+
for part in response.parts:
|
|
84
|
+
if part.inline_data and part.inline_data.data:
|
|
85
|
+
logger.info("Successfully generated image via Gemini.")
|
|
86
|
+
# Manually convert bytes to PIL Image to ensure it's a standard PIL object
|
|
87
|
+
# compatible with main.py's save(format="PNG") call.
|
|
88
|
+
return PIL.Image.open(BytesIO(part.inline_data.data))
|
|
98
89
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
url: URL of the image to download.
|
|
104
|
-
|
|
105
|
-
Raises:
|
|
106
|
-
httpx.HTTPError: If download fails.
|
|
107
|
-
"""
|
|
108
|
-
with httpx.Client(timeout=30.0) as client:
|
|
109
|
-
response = client.get(url)
|
|
110
|
-
response.raise_for_status()
|
|
111
|
-
return PIL.Image.open(BytesIO(response.content))
|
|
112
|
-
|
|
113
|
-
def generate_and_download_image(
|
|
114
|
-
self,
|
|
115
|
-
prompt: str,
|
|
116
|
-
size: Literal[
|
|
117
|
-
"1024x1024",
|
|
118
|
-
"1792x1024",
|
|
119
|
-
"1024x1792",
|
|
120
|
-
] = "1024x1024",
|
|
121
|
-
quality: Literal["standard", "hd"] = "standard",
|
|
122
|
-
) -> PIL.Image.Image | None:
|
|
123
|
-
"""Generate an image and download as PIL Image.
|
|
124
|
-
|
|
125
|
-
DALL-E returns temporary URLs that expire in 1-2 hours.
|
|
126
|
-
Use this method to download the image immediately after generation.
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
prompt: Text description of the image to generate.
|
|
130
|
-
size: Image size (default: "1024x1024").
|
|
131
|
-
quality: Image quality "standard" or "hd" (default: "standard").
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
PIL Image object, or None if generation fails.
|
|
90
|
+
logger.warning("No image data found in Gemini response.")
|
|
91
|
+
return None
|
|
135
92
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
httpx.HTTPError: If image download fails.
|
|
139
|
-
"""
|
|
140
|
-
result = self.generate_image(prompt, size, quality)
|
|
141
|
-
if result is None:
|
|
93
|
+
except genai_errors.APIError as e:
|
|
94
|
+
logger.error("Gemini API error: code=%s, message=%s", e.code, e.message)
|
|
142
95
|
return None
|
|
143
96
|
|
|
144
|
-
|
|
145
|
-
|
|
97
|
+
# Legacy methods for compatibility if needed (can be removed or shimmed)
|
|
98
|
+
def generate_image(self, *args, **kwargs): # pylint: disable=unused-argument
|
|
99
|
+
"""Deprecated: Use generate_and_download_image instead."""
|
|
100
|
+
logger.warning("generate_image is deprecated for Gemini service.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-aidol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Requires-Dist: aioia-core (>=2.2.0,<3.0.0)
|
|
16
16
|
Requires-Dist: fastapi (>=0.115.12,<0.116.0)
|
|
17
|
+
Requires-Dist: google-genai (>=1.60.0,<2.0.0)
|
|
17
18
|
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
18
19
|
Requires-Dist: openai (>=1.0.0)
|
|
19
20
|
Requires-Dist: pillow (>=10.0.0,<11.0.0)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
aidol/__init__.py,sha256=iMN-aij1k6vElJt0sZQT4QYJjvoD27Q9vtZkQA0TY9c,141
|
|
2
|
+
aidol/api/__init__.py,sha256=skD_w82nT0v1hdKK9BBOycNERexIr8F1BmSmSono4Jk,276
|
|
3
|
+
aidol/api/aidol.py,sha256=8fRkYq6-pEVKpQWbamv45IT2-b9FIgilSoPWg3itiK0,6486
|
|
4
|
+
aidol/api/common.py,sha256=R_2RjV_XjAI5TuTSXNbYEjE-wY3wC_hP2Bww0z2g5ZA,2094
|
|
5
|
+
aidol/api/companion.py,sha256=XVZum54ueskJgcaAA2Z_Z6xa3Zpy0CGrl3MIQIga-kY,10738
|
|
6
|
+
aidol/api/lead.py,sha256=RSf3GcIUVJu752rU9HG7Wy22UmnrRZnN_NGWkpTRDfE,3921
|
|
7
|
+
aidol/factories.py,sha256=5VhEbUVQEo-WruOdDauOi9xGgMxrgT339glocC1ua4o,983
|
|
8
|
+
aidol/models/__init__.py,sha256=AljQMgSE9vHx203NFQZMknKpzHIfyFLLcOMnFpMOLAs,218
|
|
9
|
+
aidol/models/aidol.py,sha256=By82BqiAasLNy8ZCNON2m46BnSCfL2J_ZFLO6_MMFO0,903
|
|
10
|
+
aidol/models/aidol_lead.py,sha256=xCa1AqJdBBeW2Edcj1pK-cXbX5oatxzCOkPyqEGBXVw,619
|
|
11
|
+
aidol/models/companion.py,sha256=fom58GXjGvAxxndS4X4MrT1HWNw8Ps99BNEyPd5JhW0,1974
|
|
12
|
+
aidol/protocols.py,sha256=8-7iwbtMv5vQUAYGRbbKvm6YfYZZ1VZz6dufse_DYp4,3375
|
|
13
|
+
aidol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
aidol/repositories/__init__.py,sha256=4kE68a-eRhXt9z5a2XCn1_2s-p3VMpQIdL3oRDb1Ns0,296
|
|
15
|
+
aidol/repositories/aidol.py,sha256=-hf7WW_sDx4vE9bHzKQnzfkgu-on7sqOem9gFO8x0EE,1810
|
|
16
|
+
aidol/repositories/aidol_lead.py,sha256=vmCHXdTtxDSiRpd_n7DuyecZDPLls56r0N5vyT_v9zI,1488
|
|
17
|
+
aidol/repositories/companion.py,sha256=dUkJA0me2kmxqk3B4L0w4ENcHeAQFw5ki6RvZ5eLHDg,2877
|
|
18
|
+
aidol/schemas/__init__.py,sha256=sNurP-s24PgS4ZJ7xusZ7Z7wXtl1rdsnAxxdeubRXHE,923
|
|
19
|
+
aidol/schemas/aidol.py,sha256=wOfHaLu4I56elLLj6A3CGriPZE4Pz4fFAyC3emtvaCE,4135
|
|
20
|
+
aidol/schemas/aidol_lead.py,sha256=JS8U-ep0Ga6x0PdwXhJfTrcOCKgG0wfFW8pN5X35GUM,1070
|
|
21
|
+
aidol/schemas/companion.py,sha256=I4hi4LT-S9AC7lqt1jyYfd0vSqYmxYNm2k9EsZdyNyM,7584
|
|
22
|
+
aidol/services/__init__.py,sha256=3vdT_CtUfeDWbsPn7Xnp41sajovcl2nCvpZ8KNFPHYM,144
|
|
23
|
+
aidol/services/companion_service.py,sha256=tNNWiIFmJQ-I3UBW06baOANXhBx5oTKoT6nkqfnDisA,2490
|
|
24
|
+
aidol/services/image_generation_service.py,sha256=fq4ua1sO4xT4BK0b-Db2u_G0lbXElbO63MFZfstOlWY,3456
|
|
25
|
+
py_aidol-0.3.0.dist-info/METADATA,sha256=xuQO_y10IvUUZe4Z-VcQkOnQ9ICHjVtSY5iVE5A0oTE,2926
|
|
26
|
+
py_aidol-0.3.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
27
|
+
py_aidol-0.3.0.dist-info/RECORD,,
|
py_aidol-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
aidol/__init__.py,sha256=iMN-aij1k6vElJt0sZQT4QYJjvoD27Q9vtZkQA0TY9c,141
|
|
2
|
-
aidol/api/__init__.py,sha256=skD_w82nT0v1hdKK9BBOycNERexIr8F1BmSmSono4Jk,276
|
|
3
|
-
aidol/api/aidol.py,sha256=DQzwF41bnfw8JpcrZlV3XDkvuH5AszyYCJwOmcRxTKg,6684
|
|
4
|
-
aidol/api/companion.py,sha256=QBsPgSjBp1USunB6Jth6fdWLwBzsI6KIq6XQZu9OifQ,6287
|
|
5
|
-
aidol/factories.py,sha256=HYkJN9qu1fZOgLW6hk3t0Ixh41mVB4tzzmOQQcso9tA,698
|
|
6
|
-
aidol/models/__init__.py,sha256=nlmLezsKx1xGdJkKANkOj3QCZQVPniXkYwQBsj8Ockk,155
|
|
7
|
-
aidol/models/aidol.py,sha256=0t1OJiJBnmhpRCYczmsr1aYD8pMZqE3wKaUWIQiymKM,750
|
|
8
|
-
aidol/models/companion.py,sha256=F64BejN3ZWP4EyNDBiV0s63dofsAgMVZJi55JV0b5gk,954
|
|
9
|
-
aidol/protocols.py,sha256=Zc9LWUcdHJlerI649v3maXbgXARiXrBiRd2cxNLnSDE,2667
|
|
10
|
-
aidol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
aidol/repositories/__init__.py,sha256=hJbn-Pw0iGMMIYkUbEqLOy-gK_0LqCJLBba21pmZ0Pw,207
|
|
12
|
-
aidol/repositories/aidol.py,sha256=zwgXB823EZsrYYoJn2S05VD1bcuhSlcwnlVCJoD09p0,1744
|
|
13
|
-
aidol/repositories/companion.py,sha256=xzAHrdCexT4cXFPTg3yCmWoqou8edeZO0A22ozMieFg,1775
|
|
14
|
-
aidol/schemas/__init__.py,sha256=dakC_xvl4akjGRSOJdFnB1k6_RbNplGnPzEwI_7wwKA,661
|
|
15
|
-
aidol/schemas/aidol.py,sha256=aTS7hx4BGUvLDMgqo3CGVY_867GO0vnAeJhRFntGG1g,3834
|
|
16
|
-
aidol/schemas/companion.py,sha256=SCjvNETAZII256o0myCp6q1wzp999JIO99q8cKf8Yo0,3224
|
|
17
|
-
aidol/services/__init__.py,sha256=3vdT_CtUfeDWbsPn7Xnp41sajovcl2nCvpZ8KNFPHYM,144
|
|
18
|
-
aidol/services/image_generation_service.py,sha256=naqOxe5jsSTs9__Nj3gwBtOQPkfWvgqVV4z6XkA6DGM,4359
|
|
19
|
-
py_aidol-0.1.0.dist-info/METADATA,sha256=_c0y1xNNGOGHmKIeSfvCqWmYLPuGfDypFHg7jn8uVoM,2880
|
|
20
|
-
py_aidol-0.1.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
21
|
-
py_aidol-0.1.0.dist-info/RECORD,,
|
|
File without changes
|