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.
@@ -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: Base + system_prompt (mutable, but sensitive)
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(..., description="Companion name")
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(CompanionBase):
82
- """Public companion response schema without sensitive fields.
183
+ class CompanionPublic(BaseModel):
184
+ """Public companion response schema for frontend.
83
185
 
84
- Excludes system_prompt for API responses.
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 aioia_core.settings import OpenAIAPISettings
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 from the Image Generation service"""
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 OpenAI DALL-E 3"""
30
+ """Service for generating images using Google Gemini 3 (Imagen)."""
31
+
32
+ client: "genai.Client | None" = None
30
33
 
31
- def __init__(self, openai_settings: OpenAIAPISettings):
34
+ def __init__(self, api_key: str | None = None, settings=None):
32
35
  """
33
- Initialize the Image Generation service with OpenAI settings.
36
+ Initialize the Image Generation service.
34
37
 
35
38
  Args:
36
- openai_settings: OpenAI settings containing required API key
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
- def generate_image(
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
- "1024x1024",
46
- "1792x1024",
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 from a text prompt using OpenAI DALL-E 3.
59
+ Generate an image using Gemini 3 and return as PIL Image.
53
60
 
54
61
  Args:
55
- prompt: Text description of the image to generate
56
- size: Image size (default: "1024x1024")
57
- quality: Image quality "standard" or "hd" (default: "standard")
62
+ prompt: Text description.
63
+ size: Ignored (Gemini specific).
64
+ quality: Ignored (Gemini specific).
58
65
 
59
66
  Returns:
60
- An ImageGenerationResponse object containing the image URL and revised prompt,
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
- try:
67
- logger.info("Generating image with OpenAI DALL-E 3...")
68
- response = self.client.images.generate(
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
- revised_prompt = image_data.revised_prompt
73
+ try:
74
+ logger.info("Generating image with Gemini 3 (prompt: %s)...", prompt[:50])
88
75
 
89
- logger.info("Successfully generated image: %s", url[:100])
90
- return ImageGenerationResponse(
91
- url=url,
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
- except openai.OpenAIError as e:
96
- logger.error("OpenAI API error: %s", e)
97
- raise
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
- def _download_image(self, url: str) -> PIL.Image.Image:
100
- """Download image from URL and return as PIL Image.
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
- Raises:
137
- openai.OpenAIError: If OpenAI API call fails.
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
- logger.info("Downloading image from DALL-E temporary URL...")
145
- return self._download_image(result.url)
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.1.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,,
@@ -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,,