vibe-aigc 0.2.0__tar.gz → 0.3.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 (45) hide show
  1. {vibe_aigc-0.2.0/vibe_aigc.egg-info → vibe_aigc-0.3.0}/PKG-INFO +2 -1
  2. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/pyproject.toml +4 -1
  3. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/__init__.py +3 -1
  4. vibe_aigc-0.3.0/vibe_aigc/comfyui.py +447 -0
  5. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/knowledge.py +177 -0
  6. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/llm.py +16 -1
  7. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0/vibe_aigc.egg-info}/PKG-INFO +2 -1
  8. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc.egg-info/SOURCES.txt +1 -0
  9. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc.egg-info/requires.txt +1 -0
  10. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/LICENSE +0 -0
  11. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/README.md +0 -0
  12. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/setup.cfg +0 -0
  13. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_adaptive_replanning.py +0 -0
  14. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_agents.py +0 -0
  15. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_assets.py +0 -0
  16. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_auto_checkpoint.py +0 -0
  17. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_automatic_checkpoints.py +0 -0
  18. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_checkpoint_serialization.py +0 -0
  19. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_error_handling.py +0 -0
  20. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_executor.py +0 -0
  21. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_feedback_system.py +0 -0
  22. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_integration.py +0 -0
  23. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_knowledge_base.py +0 -0
  24. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_metaplanner_resume.py +0 -0
  25. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_metaplanner_visualization.py +0 -0
  26. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_models.py +0 -0
  27. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_parallel_execution.py +0 -0
  28. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_planner.py +0 -0
  29. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_progress_callbacks.py +0 -0
  30. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_tools.py +0 -0
  31. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_visualization.py +0 -0
  32. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/tests/test_workflow_resume.py +0 -0
  33. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/agents.py +0 -0
  34. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/assets.py +0 -0
  35. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/cli.py +0 -0
  36. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/executor.py +0 -0
  37. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/models.py +0 -0
  38. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/persistence.py +0 -0
  39. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/planner.py +0 -0
  40. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/tools.py +0 -0
  41. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/tools_multimodal.py +0 -0
  42. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc/visualization.py +0 -0
  43. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc.egg-info/dependency_links.txt +0 -0
  44. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc.egg-info/entry_points.txt +0 -0
  45. {vibe_aigc-0.2.0 → vibe_aigc-0.3.0}/vibe_aigc.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibe-aigc
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A New Paradigm for Content Generation via Agentic Orchestration
5
5
  Author: Vibe AIGC Contributors
6
6
  License-Expression: MIT
@@ -19,6 +19,7 @@ Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.12
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Requires-Dist: aiohttp>=3.9.0
22
23
  Requires-Dist: pydantic>=2.0.0
23
24
  Requires-Dist: openai>=1.0.0
24
25
  Provides-Extra: dev
@@ -8,7 +8,7 @@ exclude = ["tests*", "docs*", "examples*", "landing*"]
8
8
 
9
9
  [project]
10
10
  name = "vibe-aigc"
11
- version = "0.2.0"
11
+ version = "0.3.0"
12
12
  description = "A New Paradigm for Content Generation via Agentic Orchestration"
13
13
  authors = [{name = "Vibe AIGC Contributors"}]
14
14
  license = "MIT"
@@ -24,6 +24,7 @@ classifiers = [
24
24
  "Typing :: Typed",
25
25
  ]
26
26
  dependencies = [
27
+ "aiohttp>=3.9.0",
27
28
  "pydantic>=2.0.0",
28
29
  "openai>=1.0.0",
29
30
  ]
@@ -65,3 +66,5 @@ python_version = "3.12"
65
66
  warn_return_any = true
66
67
  warn_unused_configs = true
67
68
  ignore_missing_imports = true
69
+
70
+
@@ -98,4 +98,6 @@ __all__ = [
98
98
  "create_default_agents",
99
99
  # Asset Bank
100
100
  "AssetBank", "Character", "StyleGuide", "Artifact", "create_asset_bank"
101
- ]
101
+ ]
102
+ # ComfyUI backend for actual image generation
103
+ from .comfyui import ComfyUIBackend, ComfyUIConfig, ComfyUIImageTool, create_comfyui_registry
@@ -0,0 +1,447 @@
1
+ """ComfyUI backend for actual image/video generation.
2
+
3
+ This implements the paper's vision of AIGC - AI Generated Content,
4
+ not just text orchestration but actual multimodal content generation.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import uuid
10
+ import aiohttp
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional, Union
13
+ from dataclasses import dataclass, field
14
+
15
+
16
+ @dataclass
17
+ class ComfyUIConfig:
18
+ """Configuration for ComfyUI connection."""
19
+ host: str = "127.0.0.1"
20
+ port: int = 8188
21
+ output_dir: Optional[str] = None # Where to save generated images
22
+
23
+ @property
24
+ def base_url(self) -> str:
25
+ return f"http://{self.host}:{self.port}"
26
+
27
+
28
+ @dataclass
29
+ class GenerationResult:
30
+ """Result from a ComfyUI generation."""
31
+ success: bool
32
+ images: List[str] = field(default_factory=list) # Paths or URLs to generated images
33
+ prompt_id: Optional[str] = None
34
+ error: Optional[str] = None
35
+ metadata: Dict[str, Any] = field(default_factory=dict)
36
+
37
+
38
+ class ComfyUIBackend:
39
+ """Backend for interacting with ComfyUI for image/video generation.
40
+
41
+ This enables vibe-aigc to generate actual content, not just orchestrate.
42
+ """
43
+
44
+ def __init__(self, config: Optional[ComfyUIConfig] = None):
45
+ self.config = config or ComfyUIConfig()
46
+ self._client_id = str(uuid.uuid4())
47
+
48
+ async def is_available(self) -> bool:
49
+ """Check if ComfyUI is running and accessible."""
50
+ try:
51
+ async with aiohttp.ClientSession() as session:
52
+ async with session.get(
53
+ f"{self.config.base_url}/system_stats",
54
+ timeout=aiohttp.ClientTimeout(total=5)
55
+ ) as resp:
56
+ return resp.status == 200
57
+ except Exception:
58
+ return False
59
+
60
+ async def get_system_stats(self) -> Dict[str, Any]:
61
+ """Get ComfyUI system stats including GPU info."""
62
+ async with aiohttp.ClientSession() as session:
63
+ async with session.get(f"{self.config.base_url}/system_stats") as resp:
64
+ return await resp.json()
65
+
66
+ async def get_available_models(self) -> Dict[str, List[str]]:
67
+ """Get available models (checkpoints, loras, etc.)."""
68
+ async with aiohttp.ClientSession() as session:
69
+ async with session.get(f"{self.config.base_url}/object_info") as resp:
70
+ obj_info = await resp.json()
71
+
72
+ models = {}
73
+
74
+ # Extract checkpoint models
75
+ if "CheckpointLoaderSimple" in obj_info:
76
+ ckpt_info = obj_info["CheckpointLoaderSimple"]
77
+ if "input" in ckpt_info and "required" in ckpt_info["input"]:
78
+ ckpt_input = ckpt_info["input"]["required"].get("ckpt_name", [])
79
+ if isinstance(ckpt_input, list) and len(ckpt_input) > 0:
80
+ if isinstance(ckpt_input[0], list):
81
+ models["checkpoints"] = ckpt_input[0]
82
+
83
+ return models
84
+
85
+ async def generate_image(
86
+ self,
87
+ prompt: str,
88
+ negative_prompt: str = "",
89
+ width: int = 512,
90
+ height: int = 512,
91
+ steps: int = 20,
92
+ cfg: float = 7.0,
93
+ seed: Optional[int] = None,
94
+ checkpoint: Optional[str] = None,
95
+ ) -> GenerationResult:
96
+ """Generate an image using ComfyUI's txt2img workflow.
97
+
98
+ Args:
99
+ prompt: Positive prompt describing what to generate
100
+ negative_prompt: What to avoid in the generation
101
+ width: Image width
102
+ height: Image height
103
+ steps: Number of sampling steps
104
+ cfg: Classifier-free guidance scale
105
+ seed: Random seed (None for random)
106
+ checkpoint: Model checkpoint to use (None for default)
107
+
108
+ Returns:
109
+ GenerationResult with paths to generated images
110
+ """
111
+ if seed is None:
112
+ import random
113
+ seed = random.randint(0, 2**32 - 1)
114
+
115
+ # Build the workflow
116
+ workflow = self._build_txt2img_workflow(
117
+ prompt=prompt,
118
+ negative_prompt=negative_prompt,
119
+ width=width,
120
+ height=height,
121
+ steps=steps,
122
+ cfg=cfg,
123
+ seed=seed,
124
+ checkpoint=checkpoint
125
+ )
126
+
127
+ return await self._execute_workflow(workflow)
128
+
129
+ async def generate_image_from_image(
130
+ self,
131
+ image_path: str,
132
+ prompt: str,
133
+ negative_prompt: str = "",
134
+ denoise: float = 0.75,
135
+ steps: int = 20,
136
+ cfg: float = 7.0,
137
+ seed: Optional[int] = None,
138
+ ) -> GenerationResult:
139
+ """Generate an image using img2img workflow.
140
+
141
+ Args:
142
+ image_path: Path to input image
143
+ prompt: Positive prompt
144
+ negative_prompt: Negative prompt
145
+ denoise: Denoising strength (0-1, higher = more change)
146
+ steps: Sampling steps
147
+ cfg: Guidance scale
148
+ seed: Random seed
149
+
150
+ Returns:
151
+ GenerationResult with paths to generated images
152
+ """
153
+ # TODO: Implement img2img workflow
154
+ # This requires uploading the image first via /upload/image
155
+ raise NotImplementedError("img2img not yet implemented")
156
+
157
+ def _build_txt2img_workflow(
158
+ self,
159
+ prompt: str,
160
+ negative_prompt: str,
161
+ width: int,
162
+ height: int,
163
+ steps: int,
164
+ cfg: float,
165
+ seed: int,
166
+ checkpoint: Optional[str] = None
167
+ ) -> Dict[str, Any]:
168
+ """Build a txt2img workflow in ComfyUI API format."""
169
+
170
+ # Default checkpoint if not specified
171
+ if checkpoint is None:
172
+ checkpoint = "v1-5-pruned-emaonly.safetensors"
173
+
174
+ workflow = {
175
+ "3": {
176
+ "class_type": "KSampler",
177
+ "inputs": {
178
+ "cfg": cfg,
179
+ "denoise": 1,
180
+ "latent_image": ["5", 0],
181
+ "model": ["4", 0],
182
+ "negative": ["7", 0],
183
+ "positive": ["6", 0],
184
+ "sampler_name": "euler",
185
+ "scheduler": "normal",
186
+ "seed": seed,
187
+ "steps": steps
188
+ }
189
+ },
190
+ "4": {
191
+ "class_type": "CheckpointLoaderSimple",
192
+ "inputs": {
193
+ "ckpt_name": checkpoint
194
+ }
195
+ },
196
+ "5": {
197
+ "class_type": "EmptyLatentImage",
198
+ "inputs": {
199
+ "batch_size": 1,
200
+ "height": height,
201
+ "width": width
202
+ }
203
+ },
204
+ "6": {
205
+ "class_type": "CLIPTextEncode",
206
+ "inputs": {
207
+ "clip": ["4", 1],
208
+ "text": prompt
209
+ }
210
+ },
211
+ "7": {
212
+ "class_type": "CLIPTextEncode",
213
+ "inputs": {
214
+ "clip": ["4", 1],
215
+ "text": negative_prompt
216
+ }
217
+ },
218
+ "8": {
219
+ "class_type": "VAEDecode",
220
+ "inputs": {
221
+ "samples": ["3", 0],
222
+ "vae": ["4", 2]
223
+ }
224
+ },
225
+ "9": {
226
+ "class_type": "SaveImage",
227
+ "inputs": {
228
+ "filename_prefix": "vibe_aigc",
229
+ "images": ["8", 0]
230
+ }
231
+ }
232
+ }
233
+
234
+ return workflow
235
+
236
+ async def _execute_workflow(self, workflow: Dict[str, Any]) -> GenerationResult:
237
+ """Execute a workflow and wait for completion."""
238
+ prompt_id = str(uuid.uuid4())
239
+
240
+ payload = {
241
+ "prompt": workflow,
242
+ "client_id": self._client_id
243
+ }
244
+
245
+ try:
246
+ async with aiohttp.ClientSession() as session:
247
+ # Queue the prompt
248
+ async with session.post(
249
+ f"{self.config.base_url}/prompt",
250
+ json=payload
251
+ ) as resp:
252
+ if resp.status != 200:
253
+ error_text = await resp.text()
254
+ return GenerationResult(
255
+ success=False,
256
+ error=f"Failed to queue prompt: {error_text}"
257
+ )
258
+ result = await resp.json()
259
+ prompt_id = result.get("prompt_id", prompt_id)
260
+
261
+ # Wait for completion by polling history
262
+ images = await self._wait_for_completion(session, prompt_id)
263
+
264
+ return GenerationResult(
265
+ success=True,
266
+ images=images,
267
+ prompt_id=prompt_id,
268
+ metadata={"workflow": workflow}
269
+ )
270
+
271
+ except Exception as e:
272
+ return GenerationResult(
273
+ success=False,
274
+ error=str(e)
275
+ )
276
+
277
+ async def _wait_for_completion(
278
+ self,
279
+ session: aiohttp.ClientSession,
280
+ prompt_id: str,
281
+ timeout: float = 300,
282
+ poll_interval: float = 0.5
283
+ ) -> List[str]:
284
+ """Wait for a prompt to complete and return output images."""
285
+ import time
286
+ start_time = time.time()
287
+
288
+ while time.time() - start_time < timeout:
289
+ async with session.get(f"{self.config.base_url}/history/{prompt_id}") as resp:
290
+ if resp.status == 200:
291
+ history = await resp.json()
292
+
293
+ if prompt_id in history:
294
+ prompt_data = history[prompt_id]
295
+
296
+ # Check if completed
297
+ if prompt_data.get("status", {}).get("completed", False):
298
+ # Extract image paths from outputs
299
+ images = []
300
+ outputs = prompt_data.get("outputs", {})
301
+
302
+ for node_id, node_output in outputs.items():
303
+ if "images" in node_output:
304
+ for img in node_output["images"]:
305
+ filename = img.get("filename")
306
+ subfolder = img.get("subfolder", "")
307
+ if filename:
308
+ # Build the view URL
309
+ img_url = f"{self.config.base_url}/view?filename={filename}"
310
+ if subfolder:
311
+ img_url += f"&subfolder={subfolder}"
312
+ images.append(img_url)
313
+
314
+ return images
315
+
316
+ await asyncio.sleep(poll_interval)
317
+
318
+ raise TimeoutError(f"Generation timed out after {timeout}s")
319
+
320
+ async def download_image(self, url: str, output_path: str) -> str:
321
+ """Download a generated image to a local file."""
322
+ async with aiohttp.ClientSession() as session:
323
+ async with session.get(url) as resp:
324
+ if resp.status == 200:
325
+ content = await resp.read()
326
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
327
+ with open(output_path, "wb") as f:
328
+ f.write(content)
329
+ return output_path
330
+ else:
331
+ raise RuntimeError(f"Failed to download image: {resp.status}")
332
+
333
+
334
+ # Integration with vibe-aigc tools system
335
+ from .tools import BaseTool, ToolResult, ToolSpec, ToolCategory
336
+
337
+
338
+ class ComfyUIImageTool(BaseTool):
339
+ """Tool for generating images via ComfyUI.
340
+
341
+ This is the actual content generation that makes vibe-aigc
342
+ a true AIGC system, not just text orchestration.
343
+ """
344
+
345
+ def __init__(self, config: Optional[ComfyUIConfig] = None):
346
+ self.backend = ComfyUIBackend(config)
347
+ self._spec = ToolSpec(
348
+ name="comfyui_image",
349
+ description="Generate images using ComfyUI (local Stable Diffusion)",
350
+ category=ToolCategory.IMAGE,
351
+ input_schema={
352
+ "type": "object",
353
+ "required": ["prompt"],
354
+ "properties": {
355
+ "prompt": {"type": "string", "description": "Image generation prompt"},
356
+ "negative_prompt": {"type": "string", "description": "What to avoid"},
357
+ "width": {"type": "integer", "default": 512},
358
+ "height": {"type": "integer", "default": 512},
359
+ "steps": {"type": "integer", "default": 20},
360
+ "cfg": {"type": "number", "default": 7.0},
361
+ "seed": {"type": "integer", "description": "Random seed"},
362
+ "checkpoint": {"type": "string", "description": "Model checkpoint"}
363
+ }
364
+ },
365
+ output_schema={
366
+ "type": "object",
367
+ "properties": {
368
+ "images": {"type": "array", "items": {"type": "string"}},
369
+ "prompt_id": {"type": "string"}
370
+ }
371
+ }
372
+ )
373
+
374
+ @property
375
+ def spec(self) -> ToolSpec:
376
+ return self._spec
377
+
378
+ async def execute(
379
+ self,
380
+ inputs: Dict[str, Any],
381
+ context: Optional[Dict[str, Any]] = None
382
+ ) -> ToolResult:
383
+ """Execute image generation.
384
+
385
+ Args:
386
+ inputs: Must contain 'prompt', optionally:
387
+ - negative_prompt
388
+ - width, height
389
+ - steps, cfg
390
+ - seed
391
+ - checkpoint
392
+ """
393
+ prompt = inputs.get("prompt", "")
394
+ if not prompt:
395
+ return ToolResult(
396
+ success=False,
397
+ output=None,
398
+ error="No prompt provided"
399
+ )
400
+
401
+ # Check if ComfyUI is available
402
+ if not await self.backend.is_available():
403
+ return ToolResult(
404
+ success=False,
405
+ output=None,
406
+ error="ComfyUI is not running. Start it at http://127.0.0.1:8188"
407
+ )
408
+
409
+ result = await self.backend.generate_image(
410
+ prompt=prompt,
411
+ negative_prompt=inputs.get("negative_prompt", ""),
412
+ width=inputs.get("width", 512),
413
+ height=inputs.get("height", 512),
414
+ steps=inputs.get("steps", 20),
415
+ cfg=inputs.get("cfg", 7.0),
416
+ seed=inputs.get("seed"),
417
+ checkpoint=inputs.get("checkpoint")
418
+ )
419
+
420
+ if result.success:
421
+ return ToolResult(
422
+ success=True,
423
+ output={
424
+ "images": result.images,
425
+ "prompt_id": result.prompt_id
426
+ },
427
+ metadata=result.metadata
428
+ )
429
+ else:
430
+ return ToolResult(
431
+ success=False,
432
+ output=None,
433
+ error=result.error
434
+ )
435
+
436
+
437
+ def create_comfyui_registry(config: Optional[ComfyUIConfig] = None):
438
+ """Create a tool registry with ComfyUI tools.
439
+
440
+ This adds actual content generation capabilities to vibe-aigc.
441
+ """
442
+ from .tools import ToolRegistry
443
+
444
+ registry = ToolRegistry()
445
+ registry.register(ComfyUIImageTool(config))
446
+
447
+ return registry
@@ -63,6 +63,7 @@ class KnowledgeBase:
63
63
  self._load_writing_knowledge()
64
64
  self._load_design_knowledge()
65
65
  self._load_music_knowledge()
66
+ self._load_visual_generation_knowledge()
66
67
 
67
68
  def _load_film_knowledge(self) -> None:
68
69
  """Film/video production knowledge."""
@@ -269,6 +270,182 @@ class KnowledgeBase:
269
270
 
270
271
  self._domains["music"] = music
271
272
 
273
+ def _load_visual_generation_knowledge(self) -> None:
274
+ """Visual/image generation knowledge for Stable Diffusion & ComfyUI."""
275
+ visual = DomainKnowledge(domain="visual")
276
+
277
+ # === AESTHETIC STYLES ===
278
+ visual.add_concept(
279
+ name="cyberpunk",
280
+ description="Futuristic dystopian aesthetic with neon and tech",
281
+ technical_specs={
282
+ "sd_prompt_tags": ["cyberpunk", "neon lights", "futuristic city", "rain",
283
+ "holographic displays", "chrome", "dystopian"],
284
+ "color_palette": ["#FF00FF", "#00FFFF", "#FF0080", "#00FF80", "#1a1a2e"],
285
+ "lighting": ["neon glow", "rim lighting", "volumetric fog", "light rays"],
286
+ "composition": ["low angle", "wide shot", "reflective surfaces"],
287
+ "negative_prompt": ["natural lighting", "daytime", "pastoral", "bright colors"]
288
+ },
289
+ examples=["Blade Runner", "Ghost in the Shell", "Akira"]
290
+ )
291
+
292
+ visual.add_concept(
293
+ name="neon noir",
294
+ description="Modern noir with neon lighting and rain-slicked streets",
295
+ technical_specs={
296
+ "sd_prompt_tags": ["neon noir", "rain", "night city", "wet streets",
297
+ "reflections", "moody", "cinematic lighting"],
298
+ "color_palette": ["#FF00FF", "#00FFFF", "#1a1a2e", "#2d132c"],
299
+ "lighting": ["neon signs", "puddle reflections", "dramatic shadows", "rim light"],
300
+ "composition": ["dutch angle", "silhouettes", "deep shadows"],
301
+ "negative_prompt": ["bright", "cheerful", "daytime", "sunny"]
302
+ }
303
+ )
304
+
305
+ visual.add_concept(
306
+ name="anime",
307
+ description="Japanese animation style",
308
+ technical_specs={
309
+ "sd_prompt_tags": ["anime", "cel shading", "vibrant colors",
310
+ "detailed eyes", "dramatic pose"],
311
+ "models": ["anything-v5", "counterfeit-v3", "waifu-diffusion"],
312
+ "cfg_scale": 7,
313
+ "negative_prompt": ["realistic", "photographic", "3d render", "western"]
314
+ }
315
+ )
316
+
317
+ visual.add_concept(
318
+ name="photorealistic",
319
+ description="Realistic, photo-quality images",
320
+ technical_specs={
321
+ "sd_prompt_tags": ["photorealistic", "hyperrealistic", "8k", "uhd",
322
+ "RAW photo", "dslr", "soft lighting", "high detail"],
323
+ "models": ["realistic-vision", "deliberate", "photon"],
324
+ "cfg_scale": 5,
325
+ "steps": 30,
326
+ "negative_prompt": ["cartoon", "anime", "painting", "illustration",
327
+ "drawing", "artificial", "fake"]
328
+ }
329
+ )
330
+
331
+ visual.add_concept(
332
+ name="cinematic",
333
+ description="Movie-quality visuals with dramatic lighting",
334
+ technical_specs={
335
+ "sd_prompt_tags": ["cinematic", "dramatic lighting", "film grain",
336
+ "anamorphic", "depth of field", "bokeh", "lens flare"],
337
+ "aspect_ratio": "21:9 or 16:9",
338
+ "lighting": ["three-point lighting", "golden hour", "blue hour", "volumetric"],
339
+ "composition": ["rule of thirds", "leading lines", "depth layers"]
340
+ }
341
+ )
342
+
343
+ visual.add_concept(
344
+ name="concept art",
345
+ description="Professional concept art style for games/films",
346
+ technical_specs={
347
+ "sd_prompt_tags": ["concept art", "digital painting", "artstation",
348
+ "trending on artstation", "matte painting", "detailed"],
349
+ "artists": ["greg rutkowski", "craig mullins", "feng zhu"],
350
+ "negative_prompt": ["photo", "realistic", "amateur"]
351
+ }
352
+ )
353
+
354
+ visual.add_concept(
355
+ name="portrait",
356
+ description="Character portrait focus",
357
+ technical_specs={
358
+ "sd_prompt_tags": ["portrait", "headshot", "face focus", "detailed face",
359
+ "sharp focus", "studio lighting"],
360
+ "composition": ["centered", "eye level", "close-up"],
361
+ "lighting": ["rembrandt lighting", "butterfly lighting", "split lighting"],
362
+ "negative_prompt": ["full body", "wide shot", "multiple people", "crowd"]
363
+ }
364
+ )
365
+
366
+ # === QUALITY MODIFIERS ===
367
+ visual.add_concept(
368
+ name="high quality",
369
+ description="Quality boosting prompt modifiers",
370
+ technical_specs={
371
+ "sd_prompt_tags": ["masterpiece", "best quality", "highly detailed",
372
+ "sharp focus", "intricate details", "professional"],
373
+ "negative_prompt": ["worst quality", "low quality", "blurry", "jpeg artifacts",
374
+ "watermark", "signature", "text", "error", "cropped"]
375
+ }
376
+ )
377
+
378
+ # === LIGHTING TECHNIQUES ===
379
+ visual.add_technique("dramatic lighting", [
380
+ "Add 'dramatic lighting' or 'cinematic lighting' to prompt",
381
+ "Include specific light source (neon, golden hour, etc.)",
382
+ "Add 'volumetric lighting' or 'god rays' for atmosphere",
383
+ "Use high contrast in composition"
384
+ ])
385
+
386
+ visual.add_technique("neon lighting", [
387
+ "Specify neon colors: 'pink neon', 'cyan neon', 'purple neon'",
388
+ "Add 'neon glow', 'neon signs', 'neon reflections'",
389
+ "Include 'wet streets' or 'rain' for reflections",
390
+ "Set scene at night for contrast"
391
+ ])
392
+
393
+ # === COMFYUI WORKFLOW KNOWLEDGE ===
394
+ visual.add_concept(
395
+ name="txt2img",
396
+ description="Text to image generation workflow",
397
+ technical_specs={
398
+ "nodes": ["CheckpointLoaderSimple", "CLIPTextEncode", "KSampler",
399
+ "VAEDecode", "SaveImage"],
400
+ "optimal_settings": {
401
+ "steps": "20-30 for quality, 10-15 for speed",
402
+ "cfg": "7-8 for balanced, 5-6 for creative, 10+ for strict",
403
+ "sampler": "euler_ancestral for variety, dpm++ for quality"
404
+ }
405
+ }
406
+ )
407
+
408
+ visual.add_concept(
409
+ name="img2img",
410
+ description="Image to image transformation workflow",
411
+ technical_specs={
412
+ "nodes": ["LoadImage", "VAEEncode", "KSampler", "VAEDecode", "SaveImage"],
413
+ "optimal_settings": {
414
+ "denoise": "0.4-0.6 for subtle changes, 0.7-0.9 for major changes",
415
+ "steps": "20-30",
416
+ "cfg": "7-10"
417
+ }
418
+ }
419
+ )
420
+
421
+ visual.add_concept(
422
+ name="character consistency",
423
+ description="Maintaining character appearance across generations",
424
+ technical_specs={
425
+ "techniques": ["IPAdapter", "reference image", "trained LoRA"],
426
+ "nodes": ["IPAdapter", "IPAdapterFaceID", "ControlNet"],
427
+ "tips": ["Use same seed for similar poses", "Train character LoRA for best results"]
428
+ }
429
+ )
430
+
431
+ # === CONSTRAINTS ===
432
+ visual.add_constraint("8gb vram", [
433
+ "Use SD 1.5 or SDXL with optimizations",
434
+ "Enable --lowvram or --medvram flags",
435
+ "Max resolution ~768x768 for SD1.5, ~1024x1024 for SDXL",
436
+ "Use fp8 for Flux models",
437
+ "Avoid large batch sizes"
438
+ ])
439
+
440
+ visual.add_constraint("video generation", [
441
+ "AnimateDiff for short clips (16-32 frames)",
442
+ "CogVideoX for longer content (requires more VRAM)",
443
+ "Keep consistent seed and prompt for coherence",
444
+ "Use motion LoRA for specific movements"
445
+ ])
446
+
447
+ self._domains["visual"] = visual
448
+
272
449
  def register_domain(self, knowledge: DomainKnowledge) -> None:
273
450
  """Register a custom domain knowledge module."""
274
451
  self._domains[knowledge.domain.lower()] = knowledge
@@ -16,6 +16,7 @@ class LLMConfig(BaseModel):
16
16
  temperature: float = 0.7
17
17
  max_tokens: int = 2000
18
18
  api_key: Optional[str] = None
19
+ base_url: Optional[str] = None # Custom endpoint (e.g., z.ai, local models)
19
20
 
20
21
 
21
22
  class LLMClient:
@@ -24,7 +25,10 @@ class LLMClient:
24
25
  def __init__(self, config: Optional[LLMConfig] = None):
25
26
  self.config = config or LLMConfig()
26
27
  try:
27
- self.client = AsyncOpenAI(api_key=self.config.api_key)
28
+ client_kwargs = {"api_key": self.config.api_key}
29
+ if self.config.base_url:
30
+ client_kwargs["base_url"] = self.config.base_url
31
+ self.client = AsyncOpenAI(**client_kwargs)
28
32
  except Exception as e:
29
33
  if "api_key" in str(e).lower():
30
34
  raise RuntimeError(
@@ -123,6 +127,17 @@ Focus on logical decomposition and clear dependencies. Keep tasks atomic and exe
123
127
  "the request was filtered. Please try again or adjust your vibe."
124
128
  )
125
129
 
130
+ # Strip markdown code blocks if present (common with some LLMs like z.ai/GLM)
131
+ content = content.strip()
132
+ if content.startswith("```"):
133
+ # Remove opening ```json or ```
134
+ first_newline = content.find("\n")
135
+ if first_newline != -1:
136
+ content = content[first_newline + 1:]
137
+ # Remove closing ```
138
+ if content.endswith("```"):
139
+ content = content[:-3].strip()
140
+
126
141
  return json.loads(content)
127
142
 
128
143
  except json.JSONDecodeError as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibe-aigc
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A New Paradigm for Content Generation via Agentic Orchestration
5
5
  Author: Vibe AIGC Contributors
6
6
  License-Expression: MIT
@@ -19,6 +19,7 @@ Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.12
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
+ Requires-Dist: aiohttp>=3.9.0
22
23
  Requires-Dist: pydantic>=2.0.0
23
24
  Requires-Dist: openai>=1.0.0
24
25
  Provides-Extra: dev
@@ -25,6 +25,7 @@ vibe_aigc/__init__.py
25
25
  vibe_aigc/agents.py
26
26
  vibe_aigc/assets.py
27
27
  vibe_aigc/cli.py
28
+ vibe_aigc/comfyui.py
28
29
  vibe_aigc/executor.py
29
30
  vibe_aigc/knowledge.py
30
31
  vibe_aigc/llm.py
@@ -1,3 +1,4 @@
1
+ aiohttp>=3.9.0
1
2
  pydantic>=2.0.0
2
3
  openai>=1.0.0
3
4
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes