comfygit-core 0.2.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.
- comfygit_core/analyzers/custom_node_scanner.py +109 -0
- comfygit_core/analyzers/git_change_parser.py +156 -0
- comfygit_core/analyzers/model_scanner.py +318 -0
- comfygit_core/analyzers/node_classifier.py +58 -0
- comfygit_core/analyzers/node_git_analyzer.py +77 -0
- comfygit_core/analyzers/status_scanner.py +362 -0
- comfygit_core/analyzers/workflow_dependency_parser.py +143 -0
- comfygit_core/caching/__init__.py +16 -0
- comfygit_core/caching/api_cache.py +210 -0
- comfygit_core/caching/base.py +212 -0
- comfygit_core/caching/comfyui_cache.py +100 -0
- comfygit_core/caching/custom_node_cache.py +320 -0
- comfygit_core/caching/workflow_cache.py +797 -0
- comfygit_core/clients/__init__.py +4 -0
- comfygit_core/clients/civitai_client.py +412 -0
- comfygit_core/clients/github_client.py +349 -0
- comfygit_core/clients/registry_client.py +230 -0
- comfygit_core/configs/comfyui_builtin_nodes.py +1614 -0
- comfygit_core/configs/comfyui_models.py +62 -0
- comfygit_core/configs/model_config.py +151 -0
- comfygit_core/constants.py +82 -0
- comfygit_core/core/environment.py +1635 -0
- comfygit_core/core/workspace.py +898 -0
- comfygit_core/factories/environment_factory.py +419 -0
- comfygit_core/factories/uv_factory.py +61 -0
- comfygit_core/factories/workspace_factory.py +109 -0
- comfygit_core/infrastructure/sqlite_manager.py +156 -0
- comfygit_core/integrations/__init__.py +7 -0
- comfygit_core/integrations/uv_command.py +318 -0
- comfygit_core/logging/logging_config.py +15 -0
- comfygit_core/managers/environment_git_orchestrator.py +316 -0
- comfygit_core/managers/environment_model_manager.py +296 -0
- comfygit_core/managers/export_import_manager.py +116 -0
- comfygit_core/managers/git_manager.py +667 -0
- comfygit_core/managers/model_download_manager.py +252 -0
- comfygit_core/managers/model_symlink_manager.py +166 -0
- comfygit_core/managers/node_manager.py +1378 -0
- comfygit_core/managers/pyproject_manager.py +1321 -0
- comfygit_core/managers/user_content_symlink_manager.py +436 -0
- comfygit_core/managers/uv_project_manager.py +569 -0
- comfygit_core/managers/workflow_manager.py +1944 -0
- comfygit_core/models/civitai.py +432 -0
- comfygit_core/models/commit.py +18 -0
- comfygit_core/models/environment.py +293 -0
- comfygit_core/models/exceptions.py +378 -0
- comfygit_core/models/manifest.py +132 -0
- comfygit_core/models/node_mapping.py +201 -0
- comfygit_core/models/protocols.py +248 -0
- comfygit_core/models/registry.py +63 -0
- comfygit_core/models/shared.py +356 -0
- comfygit_core/models/sync.py +42 -0
- comfygit_core/models/system.py +204 -0
- comfygit_core/models/workflow.py +914 -0
- comfygit_core/models/workspace_config.py +71 -0
- comfygit_core/py.typed +0 -0
- comfygit_core/repositories/migrate_paths.py +49 -0
- comfygit_core/repositories/model_repository.py +958 -0
- comfygit_core/repositories/node_mappings_repository.py +246 -0
- comfygit_core/repositories/workflow_repository.py +57 -0
- comfygit_core/repositories/workspace_config_repository.py +121 -0
- comfygit_core/resolvers/global_node_resolver.py +459 -0
- comfygit_core/resolvers/model_resolver.py +250 -0
- comfygit_core/services/import_analyzer.py +218 -0
- comfygit_core/services/model_downloader.py +422 -0
- comfygit_core/services/node_lookup_service.py +251 -0
- comfygit_core/services/registry_data_manager.py +161 -0
- comfygit_core/strategies/__init__.py +4 -0
- comfygit_core/strategies/auto.py +72 -0
- comfygit_core/strategies/confirmation.py +69 -0
- comfygit_core/utils/comfyui_ops.py +125 -0
- comfygit_core/utils/common.py +164 -0
- comfygit_core/utils/conflict_parser.py +232 -0
- comfygit_core/utils/dependency_parser.py +231 -0
- comfygit_core/utils/download.py +216 -0
- comfygit_core/utils/environment_cleanup.py +111 -0
- comfygit_core/utils/filesystem.py +178 -0
- comfygit_core/utils/git.py +1184 -0
- comfygit_core/utils/input_signature.py +145 -0
- comfygit_core/utils/model_categories.py +52 -0
- comfygit_core/utils/pytorch.py +71 -0
- comfygit_core/utils/requirements.py +211 -0
- comfygit_core/utils/retry.py +242 -0
- comfygit_core/utils/symlink_utils.py +119 -0
- comfygit_core/utils/system_detector.py +258 -0
- comfygit_core/utils/uuid.py +28 -0
- comfygit_core/utils/uv_error_handler.py +158 -0
- comfygit_core/utils/version.py +73 -0
- comfygit_core/utils/workflow_hash.py +90 -0
- comfygit_core/validation/resolution_tester.py +297 -0
- comfygit_core-0.2.0.dist-info/METADATA +939 -0
- comfygit_core-0.2.0.dist-info/RECORD +93 -0
- comfygit_core-0.2.0.dist-info/WHEEL +4 -0
- comfygit_core-0.2.0.dist-info/licenses/LICENSE.txt +661 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"""CivitAI API data models and enums."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ModelType(str, Enum):
|
|
8
|
+
"""CivitAI model types."""
|
|
9
|
+
CHECKPOINT = "Checkpoint"
|
|
10
|
+
TEXTUAL_INVERSION = "TextualInversion"
|
|
11
|
+
HYPERNETWORK = "Hypernetwork"
|
|
12
|
+
AESTHETIC_GRADIENT = "AestheticGradient"
|
|
13
|
+
LORA = "LORA"
|
|
14
|
+
CONTROLNET = "Controlnet"
|
|
15
|
+
POSES = "Poses"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SortOrder(str, Enum):
|
|
19
|
+
"""Sort order for model search."""
|
|
20
|
+
HIGHEST_RATED = "Highest Rated"
|
|
21
|
+
MOST_DOWNLOADED = "Most Downloaded"
|
|
22
|
+
NEWEST = "Newest"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TimePeriod(str, Enum):
|
|
26
|
+
"""Time period for sorting."""
|
|
27
|
+
ALL_TIME = "AllTime"
|
|
28
|
+
YEAR = "Year"
|
|
29
|
+
MONTH = "Month"
|
|
30
|
+
WEEK = "Week"
|
|
31
|
+
DAY = "Day"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CommercialUse(str, Enum):
|
|
35
|
+
"""Commercial use permissions."""
|
|
36
|
+
NONE = "None"
|
|
37
|
+
IMAGE = "Image"
|
|
38
|
+
RENT = "Rent"
|
|
39
|
+
SELL = "Sell"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FileFormat(str, Enum):
|
|
43
|
+
"""Model file format."""
|
|
44
|
+
SAFETENSOR = "SafeTensor"
|
|
45
|
+
PICKLE_TENSOR = "PickleTensor"
|
|
46
|
+
OTHER = "Other"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FloatPrecision(str, Enum):
|
|
50
|
+
"""Float precision."""
|
|
51
|
+
FP16 = "fp16"
|
|
52
|
+
FP32 = "fp32"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ModelSize(str, Enum):
|
|
56
|
+
"""Model size."""
|
|
57
|
+
FULL = "full"
|
|
58
|
+
PRUNED = "pruned"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class FileHashes:
|
|
63
|
+
"""File hash values for different algorithms."""
|
|
64
|
+
auto_v1: str | None = None
|
|
65
|
+
auto_v2: str | None = None
|
|
66
|
+
sha256: str | None = None
|
|
67
|
+
crc32: str | None = None
|
|
68
|
+
blake3: str | None = None
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_api_data(cls, data: dict | None) -> "FileHashes | None":
|
|
72
|
+
"""Parse from API response."""
|
|
73
|
+
if not data:
|
|
74
|
+
return None
|
|
75
|
+
return cls(
|
|
76
|
+
auto_v1=data.get("AutoV1"),
|
|
77
|
+
auto_v2=data.get("AutoV2"),
|
|
78
|
+
sha256=data.get("SHA256"),
|
|
79
|
+
crc32=data.get("CRC32"),
|
|
80
|
+
blake3=data.get("BLAKE3"),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class CivitAIFile:
|
|
86
|
+
"""Model file information."""
|
|
87
|
+
id: int
|
|
88
|
+
name: str
|
|
89
|
+
size_kb: float
|
|
90
|
+
type: str = "Model"
|
|
91
|
+
pickle_scan_result: str | None = None
|
|
92
|
+
pickle_scan_message: str | None = None
|
|
93
|
+
virus_scan_result: str | None = None
|
|
94
|
+
scanned_at: str | None = None
|
|
95
|
+
primary: bool = False
|
|
96
|
+
download_url: str | None = None
|
|
97
|
+
hashes: FileHashes | None = None
|
|
98
|
+
|
|
99
|
+
# Metadata fields
|
|
100
|
+
fp: FloatPrecision | None = None
|
|
101
|
+
size: ModelSize | None = None
|
|
102
|
+
format: FileFormat | None = None
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def from_api_data(cls, data: dict) -> "CivitAIFile":
|
|
106
|
+
"""Parse from API response."""
|
|
107
|
+
metadata = data.get("metadata", {})
|
|
108
|
+
return cls(
|
|
109
|
+
id=data.get("id", 0),
|
|
110
|
+
name=data.get("name", ""),
|
|
111
|
+
size_kb=data.get("sizeKB", 0.0),
|
|
112
|
+
type=data.get("type", "Model"),
|
|
113
|
+
pickle_scan_result=data.get("pickleScanResult"),
|
|
114
|
+
pickle_scan_message=data.get("pickleScanMessage"),
|
|
115
|
+
virus_scan_result=data.get("virusScanResult"),
|
|
116
|
+
scanned_at=data.get("scannedAt"),
|
|
117
|
+
primary=data.get("primary", False),
|
|
118
|
+
download_url=data.get("downloadUrl"),
|
|
119
|
+
hashes=FileHashes.from_api_data(data.get("hashes")),
|
|
120
|
+
fp=FloatPrecision(metadata["fp"]) if metadata.get("fp") else None,
|
|
121
|
+
size=ModelSize(metadata["size"]) if metadata.get("size") else None,
|
|
122
|
+
format=FileFormat(metadata["format"]) if metadata.get("format") else None,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def get_preferred_hash(self) -> str | None:
|
|
126
|
+
"""Get the best available hash for identification (prefers SHA256)."""
|
|
127
|
+
if not self.hashes:
|
|
128
|
+
return None
|
|
129
|
+
return (self.hashes.sha256 or self.hashes.blake3 or
|
|
130
|
+
self.hashes.auto_v2 or self.hashes.auto_v1 or
|
|
131
|
+
self.hashes.crc32)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class CivitAIImage:
|
|
136
|
+
"""Model preview image."""
|
|
137
|
+
id: str
|
|
138
|
+
url: str
|
|
139
|
+
nsfw: bool
|
|
140
|
+
width: int
|
|
141
|
+
height: int
|
|
142
|
+
hash: str
|
|
143
|
+
meta: dict | None = None
|
|
144
|
+
|
|
145
|
+
@classmethod
|
|
146
|
+
def from_api_data(cls, data: dict) -> "CivitAIImage":
|
|
147
|
+
"""Parse from API response."""
|
|
148
|
+
return cls(
|
|
149
|
+
id=str(data.get("id", "")),
|
|
150
|
+
url=data.get("url", ""),
|
|
151
|
+
nsfw=bool(data.get("nsfw", False)),
|
|
152
|
+
width=data.get("width", 0),
|
|
153
|
+
height=data.get("height", 0),
|
|
154
|
+
hash=data.get("hash", ""),
|
|
155
|
+
meta=data.get("meta"),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass
|
|
160
|
+
class CivitAIBasicModelInfo:
|
|
161
|
+
"""Basic model information (nested in version response)."""
|
|
162
|
+
name: str
|
|
163
|
+
type: str | None = None
|
|
164
|
+
nsfw: bool = False
|
|
165
|
+
poi: bool = False
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def from_api_data(cls, data: dict | None) -> "CivitAIBasicModelInfo | None":
|
|
169
|
+
"""Parse from API response."""
|
|
170
|
+
if not data:
|
|
171
|
+
return None
|
|
172
|
+
return cls(
|
|
173
|
+
name=data.get("name", ""),
|
|
174
|
+
type=data.get("type"),
|
|
175
|
+
nsfw=data.get("nsfw", False),
|
|
176
|
+
poi=data.get("poi", False),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@dataclass
|
|
181
|
+
class CivitAIModelVersion:
|
|
182
|
+
"""Model version information."""
|
|
183
|
+
id: int
|
|
184
|
+
model_id: int
|
|
185
|
+
name: str
|
|
186
|
+
description: str | None = None
|
|
187
|
+
created_at: str | None = None
|
|
188
|
+
updated_at: str | None = None
|
|
189
|
+
base_model: str | None = None
|
|
190
|
+
early_access_time_frame: int = 0
|
|
191
|
+
download_url: str | None = None
|
|
192
|
+
trained_words: list[str] | None = None
|
|
193
|
+
files: list[CivitAIFile] | None = None
|
|
194
|
+
images: list[CivitAIImage] | None = None
|
|
195
|
+
model: CivitAIBasicModelInfo | None = None
|
|
196
|
+
download_count: int = 0
|
|
197
|
+
rating_count: int = 0
|
|
198
|
+
rating: float = 0.0
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def from_api_data(cls, data: dict) -> "CivitAIModelVersion":
|
|
202
|
+
"""Parse from API response."""
|
|
203
|
+
stats = data.get("stats", {})
|
|
204
|
+
return cls(
|
|
205
|
+
id=data.get("id", 0),
|
|
206
|
+
model_id=data.get("modelId", 0),
|
|
207
|
+
name=data.get("name", ""),
|
|
208
|
+
description=data.get("description"),
|
|
209
|
+
created_at=data.get("createdAt"),
|
|
210
|
+
updated_at=data.get("updatedAt"),
|
|
211
|
+
base_model=data.get("baseModel"),
|
|
212
|
+
early_access_time_frame=data.get("earlyAccessTimeFrame", 0),
|
|
213
|
+
download_url=data.get("downloadUrl"),
|
|
214
|
+
trained_words=data.get("trainedWords", []),
|
|
215
|
+
files=[CivitAIFile.from_api_data(f) for f in data.get("files", [])],
|
|
216
|
+
images=[CivitAIImage.from_api_data(i) for i in data.get("images", [])],
|
|
217
|
+
model=CivitAIBasicModelInfo.from_api_data(data.get("model")),
|
|
218
|
+
download_count=stats.get("downloadCount", 0),
|
|
219
|
+
rating_count=stats.get("ratingCount", 0),
|
|
220
|
+
rating=stats.get("rating", 0.0),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass
|
|
225
|
+
class CivitAICreator:
|
|
226
|
+
"""Model creator information."""
|
|
227
|
+
username: str
|
|
228
|
+
image: str | None = None
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def from_api_data(cls, data: dict) -> "CivitAICreator":
|
|
232
|
+
"""Parse from API response."""
|
|
233
|
+
return cls(
|
|
234
|
+
username=data.get("username", ""),
|
|
235
|
+
image=data.get("image"),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass
|
|
240
|
+
class CivitAIModel:
|
|
241
|
+
"""CivitAI model information."""
|
|
242
|
+
id: int
|
|
243
|
+
name: str
|
|
244
|
+
description: str | None = None
|
|
245
|
+
type: ModelType | None = None
|
|
246
|
+
nsfw: bool = False
|
|
247
|
+
tags: list[str] | None = None
|
|
248
|
+
mode: str | None = None # "Archived" or "TakenDown"
|
|
249
|
+
creator: CivitAICreator | None = None
|
|
250
|
+
model_versions: list[CivitAIModelVersion] | None = None
|
|
251
|
+
|
|
252
|
+
# Stats
|
|
253
|
+
download_count: int = 0
|
|
254
|
+
favorite_count: int = 0
|
|
255
|
+
comment_count: int = 0
|
|
256
|
+
rating_count: int = 0
|
|
257
|
+
rating: float = 0.0
|
|
258
|
+
|
|
259
|
+
@classmethod
|
|
260
|
+
def from_api_data(cls, data: dict) -> "CivitAIModel":
|
|
261
|
+
"""Parse from API response."""
|
|
262
|
+
stats = data.get("stats", {})
|
|
263
|
+
creator_data = data.get("creator")
|
|
264
|
+
|
|
265
|
+
# Handle tags that can be either strings or objects with 'name' field
|
|
266
|
+
tags_raw = data.get("tags", [])
|
|
267
|
+
tags = []
|
|
268
|
+
for tag in tags_raw:
|
|
269
|
+
if isinstance(tag, str):
|
|
270
|
+
tags.append(tag)
|
|
271
|
+
elif isinstance(tag, dict) and "name" in tag:
|
|
272
|
+
tags.append(tag["name"])
|
|
273
|
+
|
|
274
|
+
return cls(
|
|
275
|
+
id=data.get("id", 0),
|
|
276
|
+
name=data.get("name", ""),
|
|
277
|
+
description=data.get("description"),
|
|
278
|
+
type=ModelType(data["type"]) if data.get("type") else None,
|
|
279
|
+
nsfw=data.get("nsfw", False),
|
|
280
|
+
tags=tags,
|
|
281
|
+
mode=data.get("mode"),
|
|
282
|
+
creator=CivitAICreator.from_api_data(creator_data) if creator_data else None,
|
|
283
|
+
model_versions=[
|
|
284
|
+
CivitAIModelVersion.from_api_data(v)
|
|
285
|
+
for v in data.get("modelVersions", [])
|
|
286
|
+
],
|
|
287
|
+
download_count=stats.get("downloadCount", 0),
|
|
288
|
+
favorite_count=stats.get("favoriteCount", 0),
|
|
289
|
+
comment_count=stats.get("commentCount", 0),
|
|
290
|
+
rating_count=stats.get("ratingCount", 0),
|
|
291
|
+
rating=stats.get("rating", 0.0),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def get_latest_version(self) -> CivitAIModelVersion | None:
|
|
295
|
+
"""Get the most recent model version."""
|
|
296
|
+
if not self.model_versions:
|
|
297
|
+
return None
|
|
298
|
+
return self.model_versions[0] # API returns newest first
|
|
299
|
+
|
|
300
|
+
def get_primary_file(self) -> CivitAIFile | None:
|
|
301
|
+
"""Get the primary file from the latest version."""
|
|
302
|
+
latest = self.get_latest_version()
|
|
303
|
+
if not latest or not latest.files:
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
# Find primary file or default to first
|
|
307
|
+
for file in latest.files:
|
|
308
|
+
if file.primary:
|
|
309
|
+
return file
|
|
310
|
+
return latest.files[0]
|
|
311
|
+
|
|
312
|
+
def find_file_by_hash(self, hash_value: str) -> CivitAIFile | None:
|
|
313
|
+
"""Find a file across all versions by any of its hashes."""
|
|
314
|
+
if not self.model_versions:
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
hash_upper = hash_value.upper()
|
|
318
|
+
for version in self.model_versions:
|
|
319
|
+
if not version.files:
|
|
320
|
+
continue
|
|
321
|
+
for file in version.files:
|
|
322
|
+
if not file.hashes:
|
|
323
|
+
continue
|
|
324
|
+
if (file.hashes.auto_v1 == hash_upper or
|
|
325
|
+
file.hashes.auto_v2 == hash_upper or
|
|
326
|
+
file.hashes.sha256 == hash_upper or
|
|
327
|
+
file.hashes.crc32 == hash_upper or
|
|
328
|
+
file.hashes.blake3 == hash_upper):
|
|
329
|
+
return file
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@dataclass
|
|
334
|
+
class SearchParams:
|
|
335
|
+
"""Parameters for model search."""
|
|
336
|
+
query: str | None = None
|
|
337
|
+
tag: str | None = None
|
|
338
|
+
username: str | None = None
|
|
339
|
+
types: list[ModelType] | None = None
|
|
340
|
+
sort: SortOrder | None = None
|
|
341
|
+
period: TimePeriod | None = None
|
|
342
|
+
limit: int = 20
|
|
343
|
+
page: int = 1
|
|
344
|
+
nsfw: bool | None = None
|
|
345
|
+
commercial_use: CommercialUse | None = None
|
|
346
|
+
allow_no_credit: bool | None = None
|
|
347
|
+
allow_derivatives: bool | None = None
|
|
348
|
+
allow_different_licenses: bool | None = None
|
|
349
|
+
primary_file_only: bool | None = None
|
|
350
|
+
supports_generation: bool | None = None
|
|
351
|
+
|
|
352
|
+
def to_dict(self) -> dict:
|
|
353
|
+
"""Convert to query parameters dict."""
|
|
354
|
+
params = {}
|
|
355
|
+
|
|
356
|
+
if self.query:
|
|
357
|
+
params["query"] = self.query
|
|
358
|
+
if self.tag:
|
|
359
|
+
params["tag"] = self.tag
|
|
360
|
+
if self.username:
|
|
361
|
+
params["username"] = self.username
|
|
362
|
+
if self.types:
|
|
363
|
+
# CivitAI expects multiple types as comma-separated string
|
|
364
|
+
params["types"] = ",".join([t.value for t in self.types])
|
|
365
|
+
if self.sort:
|
|
366
|
+
params["sort"] = self.sort.value
|
|
367
|
+
if self.period:
|
|
368
|
+
params["period"] = self.period.value
|
|
369
|
+
if self.limit:
|
|
370
|
+
params["limit"] = self.limit
|
|
371
|
+
# Only include page if not using query (CivitAI restriction)
|
|
372
|
+
if self.page and self.page > 1 and not self.query:
|
|
373
|
+
params["page"] = self.page
|
|
374
|
+
if self.nsfw is not None:
|
|
375
|
+
params["nsfw"] = str(self.nsfw).lower()
|
|
376
|
+
if self.commercial_use:
|
|
377
|
+
params["allowCommercialUse"] = self.commercial_use.value
|
|
378
|
+
if self.allow_no_credit is not None:
|
|
379
|
+
params["allowNoCredit"] = str(self.allow_no_credit).lower()
|
|
380
|
+
if self.allow_derivatives is not None:
|
|
381
|
+
params["allowDerivatives"] = str(self.allow_derivatives).lower()
|
|
382
|
+
if self.allow_different_licenses is not None:
|
|
383
|
+
params["allowDifferentLicenses"] = str(self.allow_different_licenses).lower()
|
|
384
|
+
if self.primary_file_only is not None:
|
|
385
|
+
params["primaryFileOnly"] = str(self.primary_file_only).lower()
|
|
386
|
+
if self.supports_generation is not None:
|
|
387
|
+
params["supportsGeneration"] = str(self.supports_generation).lower()
|
|
388
|
+
|
|
389
|
+
return params
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
@dataclass
|
|
393
|
+
class CivitAITag:
|
|
394
|
+
"""Tag information."""
|
|
395
|
+
name: str
|
|
396
|
+
model_count: int
|
|
397
|
+
link: str
|
|
398
|
+
|
|
399
|
+
@classmethod
|
|
400
|
+
def from_api_data(cls, data: dict) -> "CivitAITag":
|
|
401
|
+
"""Parse from API response."""
|
|
402
|
+
return cls(
|
|
403
|
+
name=data.get("name", ""),
|
|
404
|
+
model_count=data.get("modelCount", 0),
|
|
405
|
+
link=data.get("link", ""),
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@dataclass
|
|
410
|
+
class SearchResponse:
|
|
411
|
+
"""Model search response with pagination."""
|
|
412
|
+
items: list[CivitAIModel]
|
|
413
|
+
total_items: int
|
|
414
|
+
current_page: int
|
|
415
|
+
page_size: int
|
|
416
|
+
total_pages: int
|
|
417
|
+
next_page: str | None = None
|
|
418
|
+
prev_page: str | None = None
|
|
419
|
+
|
|
420
|
+
@classmethod
|
|
421
|
+
def from_api_data(cls, data: dict) -> "SearchResponse":
|
|
422
|
+
"""Parse from API response."""
|
|
423
|
+
metadata = data.get("metadata", {})
|
|
424
|
+
return cls(
|
|
425
|
+
items=[CivitAIModel.from_api_data(m) for m in data.get("items", [])],
|
|
426
|
+
total_items=int(metadata.get("totalItems", 0)),
|
|
427
|
+
current_page=int(metadata.get("currentPage", 1)),
|
|
428
|
+
page_size=int(metadata.get("pageSize", 20)),
|
|
429
|
+
total_pages=int(metadata.get("totalPages", 1)),
|
|
430
|
+
next_page=metadata.get("nextPage"),
|
|
431
|
+
prev_page=metadata.get("prevPage"),
|
|
432
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Commit operation result models."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..models.shared import ModelWithLocation
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class ModelResolutionRequest:
|
|
12
|
+
"""Request for resolving ambiguous model matches."""
|
|
13
|
+
workflow_name: str
|
|
14
|
+
node_id: str
|
|
15
|
+
node_type: str
|
|
16
|
+
widget_index: int
|
|
17
|
+
original_value: str
|
|
18
|
+
candidates: list[ModelWithLocation]
|