Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627ab3aec6 | ||
|
|
74eaa2a3e2 | ||
|
|
747bf5e02b | ||
|
|
8c18d0d40e | ||
|
|
bad4a24426 |
@@ -72,7 +72,7 @@ This server requires Python 3.10+ and the following packages:
|
||||
* `httpx`: For asynchronous API requests (e.g., Wikipedia).
|
||||
|
||||
```bash
|
||||
pip install uvicorn starlette Pillow requests httpx
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
+140782
File diff suppressed because it is too large
Load Diff
+8
-4
@@ -10,9 +10,9 @@ This model is not aesthetic tuned, it must be given explicit tags for everything
|
||||
Has very excellent understanding of characters and artists down to extremely niche. Unless prompting an original character, their name is enough to decribe their appearance completely except for clothing.
|
||||
Accepts a list of comma-separated booru-style tags. Use spaces, not underscores, for tags. **Use the `search_tags` tool to verify your tags**.
|
||||
Prompts MUST follow this format: <1girl/1boy/1other/solo/couple/(can use multiple)>, <character(s)>, <series>, <artist>, <tags>
|
||||
Every prompt MUST include every one of the above sections.
|
||||
Every prompt MUST include every one of the above sections (the angle brackets are not part of the prompt).
|
||||
Quality tags such as "masterpiece", "best quality", "very awa", are a LAST RESORT, they override the artist tags. If absolutely required they should be prepended.
|
||||
It understands 'implicit' artist tags such as "official art" or "game cg" for the 'artist' immediately following the series.
|
||||
It understands every artist, so pick one appropriate for the image. If desired, it also knows 'implicit' artist tags such as "official art" or "game cg" for the 'artist' immediately following the series name.
|
||||
Natural language understanding is very limited, but can do things like "dark blue skirt" or natural language order of tags such as "lying, on bed".
|
||||
Do not use a negative prompt unless explicitly required to exclude something, your first prompt should have a blank negative prompt.
|
||||
Has the SDXL problem with hands, works best if hand posture is explicitly prompted.
|
||||
@@ -35,10 +35,14 @@ preset = "xl"
|
||||
"Rescale CFG" = 0.3
|
||||
|
||||
["Z-Image-Turbo"]
|
||||
description = "General image generation model. Aesthetic tuned, gets good results first try. Can only do softcore NSFW."
|
||||
description = "General image generation model. Aesthetic tuned, gets good results first try. Can do softcore NSFW, e.g. underwear, breasts, asses. No full-frontal nudity."
|
||||
guide = """
|
||||
This model is aesthetic tuned, regenerating with the same prompt will yield essentially the same image. Change the prompt before resubmitting.
|
||||
Understands natural language very well. Characters can be described by naming them and using this name later in the prompt. Longer, detailed prompts work better.
|
||||
It's a CFG 1.0 turbo model, the negative prompt has no effect.
|
||||
Understands natural language very well, uses Qwen 3 4B as the text encoder. The longer and more detailed prompt the better, take advantage of line breaks and formatting.
|
||||
If any part of the image is left out of the prompt, it will default to generic AI slop which is most NOT what you want. Be very explicit about each character's details. Appearance, ethnicity, age, individual outfit components, facial expression, pose, action, where they are looking, position in the image, etc. should ALL be included in the prompt. Characters can be referenced by naming them and using this name later in the prompt. This also helps the model avoid mixing traits between them.
|
||||
This also applies to the image itself. Composition, framing, lighting, image style, setting, background, etc. should all be explicitly specified in the prompt.
|
||||
Has some idiosyncrasies so you may need to iterate the prompt a few times to get around some weird artifacts. Think things like "green eyes" making them glow green, or "blush" making the entire face glow. Also really wants to make shirts tucked in for some reason. Examine the generated image closely and edit the prompt if needed.
|
||||
"""
|
||||
preset = "zit"
|
||||
filename = "z_image_turbo_bf16.safetensors"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
uvicorn
|
||||
starlette
|
||||
requests
|
||||
httpx
|
||||
Pillow
|
||||
+43
-14
@@ -12,6 +12,20 @@ def strip_html(text: str) -> str:
|
||||
"""Removes HTML tags from a string using regex to provide clean text to the LLM."""
|
||||
return re.sub(r'<[^>]*>', '', text)
|
||||
|
||||
def clean_wikitext(text: str) -> str:
|
||||
"""
|
||||
Removes the most distracting elements of raw Wikitext:
|
||||
1. HTML comments (<!-- ... -->)
|
||||
2. Citations (<ref /> and <ref>...</ref>)
|
||||
"""
|
||||
# Remove HTML comments
|
||||
text = re.sub(r'<!--.*?-->', '', text, flags=re.DOTALL)
|
||||
# Remove self-closing citations FIRST to prevent them being seen as opening tags
|
||||
text = re.sub(r'<ref[^>]*/>', '', text)
|
||||
# Remove paired citations
|
||||
text = re.sub(r'<ref[^>]*>.*?</ref>', '', text, flags=re.DOTALL)
|
||||
return text
|
||||
|
||||
async def _make_request(params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Asynchronous helper to make the API request using httpx.
|
||||
@@ -62,10 +76,11 @@ async def _search(title: str, limit: int = 5, fallback: bool = False) -> str:
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
async def _fetch_content(title: str, section_index: int) -> Optional[str]:
|
||||
async def _fetch_content(title: str, section_index: int) -> tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Retrieves raw Wikitext for a specific section.
|
||||
section_index=0 returns the lead section.
|
||||
Returns a tuple of (content, final_title).
|
||||
"""
|
||||
params = {
|
||||
"action": "query",
|
||||
@@ -80,23 +95,26 @@ async def _fetch_content(title: str, section_index: int) -> Optional[str]:
|
||||
|
||||
pages = data.get("query", {}).get("pages", {})
|
||||
if not pages:
|
||||
return None
|
||||
return None, None
|
||||
|
||||
page_id = next(iter(pages))
|
||||
page = pages[page_id]
|
||||
final_title = page.get("title")
|
||||
|
||||
if "missing" in page:
|
||||
return None
|
||||
return None, None
|
||||
|
||||
revisions = page.get("revisions", [])
|
||||
if not revisions:
|
||||
return None
|
||||
return None, final_title
|
||||
|
||||
return revisions[0].get("*")
|
||||
content = revisions[0].get("*")
|
||||
return (clean_wikitext(content) if content else None), final_title
|
||||
|
||||
async def _fetch_toc(title: str) -> Optional[str]:
|
||||
async def _fetch_toc(title: str) -> tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Retrieves the Table of Contents data and formats it hierarchically.
|
||||
Returns a tuple of (toc, final_title).
|
||||
"""
|
||||
params = {
|
||||
"action": "parse",
|
||||
@@ -109,14 +127,15 @@ async def _fetch_toc(title: str) -> Optional[str]:
|
||||
|
||||
parse_data = data.get("parse")
|
||||
if not parse_data:
|
||||
return None
|
||||
return None, None
|
||||
|
||||
final_title = parse_data.get("title")
|
||||
toc_data = parse_data.get("tocdata", {})
|
||||
sections = toc_data.get("sections", [])
|
||||
if not sections:
|
||||
return "No table of contents found for this page."
|
||||
return "No table of contents found for this page.", final_title
|
||||
|
||||
lines = [f"Table of Contents for \"{parse_data.get('title', title)}\":"]
|
||||
lines = [f"Table of Contents for \"{final_title}\":"]
|
||||
for s in sections:
|
||||
level = s.get("tocLevel", 1)
|
||||
index = s.get("index")
|
||||
@@ -124,7 +143,7 @@ async def _fetch_toc(title: str) -> Optional[str]:
|
||||
indent = " " * (level - 1)
|
||||
lines.append(f"{indent}[{index}] {line}")
|
||||
|
||||
return "\n".join(lines)
|
||||
return "\n".join(lines), final_title
|
||||
|
||||
async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
@@ -139,18 +158,24 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
section_index = args.get("section_index")
|
||||
search_limit = args.get("search_limit", 5)
|
||||
|
||||
final_title = None
|
||||
|
||||
if mode == "search":
|
||||
result = await _search(title, search_limit, fallback=False)
|
||||
elif mode == "summary":
|
||||
# Lead section + ToC is the default 'summary' to guide the AI's next steps
|
||||
content = await _fetch_content(title, 0)
|
||||
content, title_from_content = await _fetch_content(title, 0)
|
||||
final_title = title_from_content
|
||||
if content is None:
|
||||
result = await _search(title, search_limit, fallback=True)
|
||||
else:
|
||||
toc = await _fetch_toc(title)
|
||||
toc, title_from_toc = await _fetch_toc(title)
|
||||
if title_from_toc:
|
||||
final_title = title_from_toc
|
||||
result = f"{content}\n\n---\n\n{toc}"
|
||||
elif mode == "toc":
|
||||
toc = await _fetch_toc(title)
|
||||
toc, title_from_toc = await _fetch_toc(title)
|
||||
final_title = title_from_toc
|
||||
if toc is None:
|
||||
result = await _search(title, search_limit, fallback=True)
|
||||
else:
|
||||
@@ -158,12 +183,16 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
elif mode == "section":
|
||||
if section_index is None:
|
||||
raise ToolError("Missing required parameter 'section_index' for mode='section'")
|
||||
content = await _fetch_content(title, int(section_index))
|
||||
content, title_from_content = await _fetch_content(title, int(section_index))
|
||||
final_title = title_from_content
|
||||
if content is None:
|
||||
result = await _search(title, search_limit, fallback=True)
|
||||
else:
|
||||
result = content
|
||||
else:
|
||||
raise ToolError(f"Invalid mode '{mode}'. Supported modes: summary, toc, section, search")
|
||||
|
||||
if final_title and final_title != title:
|
||||
result = f"Redirected to \"{final_title}\"\n\n{result}"
|
||||
|
||||
return [{"type": "text", "text": result}]
|
||||
|
||||
@@ -174,7 +174,7 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
except (ValueError, AttributeError):
|
||||
raise ToolError(f"Invalid resolution format for preset '{res_preset_name}': {res_val_str}. Expected 'WidthxHeight'.")
|
||||
else:
|
||||
raise ToolError(f"Resolution preset '{res_preset_name}' not found for this model. Available: {', '.join(res_set.keys())}")
|
||||
raise ToolError(f"Resolution preset '{res_preset_name}' not found for this model. You MUST call get_model_info with this model_name to see the available resolution presets.")
|
||||
else:
|
||||
raise ToolError(f"No resolution set configured for model '{model_name}'.")
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Available models:\n\n" + "\n".join([f"- {m['name']}: {m['description']}" for m in catalog]) +
|
||||
"\n\nTo get a detailed prompting guide and available resolutions for a specific model, call this tool again with the 'model_name' argument."
|
||||
"\n\nBefore generating an image, you MUST call this tool again with your selected model as the 'model_name' argument."
|
||||
}
|
||||
]
|
||||
|
||||
@@ -41,7 +41,7 @@ async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"type": "text",
|
||||
"text": f"Model '{model_name}' not found.\n\nAvailable models:\n\n" +
|
||||
"\n".join([f"- {m['name']}: {m['description']}" for m in catalog]) +
|
||||
"\n\nTo get a detailed prompting guide and available resolutions for a specific model, call this tool again with the 'model_name' argument."
|
||||
"\n\nBefore generating an image, you MUST call this tool again with selected model as the 'model_name' argument."
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user