image-portrait
Use this skill for all identity-consistent portrait generation requests on Starchild.
Covers: professional headshots, dating/social photos, artistic style transfers, themed/holiday portraits, photo series, digital avatars, children/family photos, ID/passport photos.
Core principle: call the provided script. Do not re-implement proxy/billing plumbing.
1. Quick start — single portrait (most common)
⚠️ Execution context — read this first.
The code blocks below are Python, not shell commands. Starchild's bash tool
runs /bin/bash -c, which cannot parse exec(open(...)) — pasting them directly
into a bash command will fail with syntax error near unexpected token 'open'.
Also, exec(open(...)) inside python3 -c fails with NameError: __file__
because the script uses __file__ for path resolution.
Use python3 - <<'EOF' with from exports import when calling via the bash tool:
python3 - <<'EOF'
import sys
sys.path.insert(0, "skills/image-portrait")
from exports import generate_portrait
result = generate_portrait(
image_path="path/to/user/photo.jpg",
style="professional",
)
print(result)
EOF
The heredoc (<<'EOF') preserves all quotes and newlines — no escaping needed.
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_portrait(
image_path="path/to/user/photo.jpg",
style="professional",
)
# result -> {"success": True, "images": [{"local_path": "output/images/..."}], ...}
The script reads the local file, base64-encodes it, and sends it to fal.ai as a data URI — no manual URL publishing needed.
2. Quick start — public URL
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_portrait(
face_image_url="https://example.com/photo.jpg",
style="anime",
)
3. Quick start — text-to-image (no reference photo)
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_portrait(
prompt="a young woman in cyberpunk armor, neon city background, rain",
model="nanopro",
)
When no image_path or face_image_url is provided, the script uses the text-to-image endpoint (no /edit suffix).
Delivering the result to the user — IMPORTANT
Never hand the user the raw fal.media URL. fal serves files with restrictive CSP headers. The only reliable delivery path is the already-downloaded local file:
- Use each image's
local_path (e.g. output/images/xxx.png) — the script always downloads on success.
- Tell the user the files are saved to
output/images/ and viewable in the workspace file panel.
- On Web channel, embed inline so the user can preview in chat:

- On Telegram / WeChat: send via
send_to_telegram(file_path="output/images/...", message_type="image") or send_to_wechat(file_path="output/images/...", message_type="image").
4. Parameters
| Parameter |
Required |
Default |
Description |
image_path |
no |
— |
Local workspace file path to the user's face photo |
face_image_url |
no |
— |
Public HTTPS URL of the user's face photo |
style |
no |
"professional" |
Preset style key (see §5) |
scene |
no |
None |
Custom scene description (appended to style prompt) |
prompt |
no |
None |
Fully custom prompt — overrides style+scene when set |
model |
no |
"nanopro" |
Model: "nano2" (fastest ~15s), "nanopro" (balanced ~25s, default), or "gpt" (best quality ~150s) |
count |
no |
1 |
Number of images to generate (1–8) |
aspect_ratio |
no |
"1:1" |
Output ratio: 1:1, 3:4, 4:3, 9:16, 16:9 |
Image input rules:
- Provide
image_path OR face_image_url for identity-consistent generation (edit mode).
- If both are given,
image_path takes priority.
- Omit both for pure text-to-image generation (generate mode).
Prompt priority: prompt > style + scene > style > default (professional).
5. Style presets
A: Identity-consistent character styles
| Style |
Key |
Best for |
| Professional headshot |
professional |
LinkedIn, resume, corporate |
| Artistic portrait |
artistic |
Creative portfolio, gallery |
| Anime |
anime |
Social media, fun avatar |
| Cyberpunk |
cyberpunk |
Gaming profile, sci-fi fan |
| Oil painting |
oil_painting |
Art gift, classical look |
| Watercolor |
watercolor |
Soft artistic portrait |
| Vintage |
vintage |
Retro aesthetic, nostalgia |
| Casual lifestyle |
casual |
Social media, personal blog |
B: Personal showcase / dating / social
| Style |
Key |
Best for |
| Dating — cafe |
dating_cafe |
Dating app, warm vibe |
| Dating — beach |
dating_beach |
Dating app, summer vibe |
| Dating — city |
dating_city |
Dating app, urban vibe |
| Dating — restaurant |
dating_restaurant |
Dating app, elegant vibe |
| Travel — Europe |
travel_europe |
Travel blog, social media |
| Travel — Japan |
travel_japan |
Travel blog, cultural |
| Travel — tropical |
travel_tropical |
Vacation, resort |
| Sports — gym |
sports_gym |
Fitness profile |
| Sports — running |
sports_running |
Athletic profile |
| Social media |
social_media |
Instagram, TikTok |
| LinkedIn |
linkedin |
Professional networking |
| Personal brand |
personal_brand |
Entrepreneur, creator |
D: Themed / scene portraits
| Style |
Key |
Best for |
| Christmas |
christmas |
Holiday greeting, social |
| Halloween |
halloween |
Holiday fun |
| Graduation |
graduation |
Milestone celebration |
| Wedding |
wedding |
Wedding planning, save-the-date |
| Business speech |
business_speech |
Speaker profile |
| Musician |
musician |
Music promotion |
| Chef |
chef |
Food blog, restaurant |
| Outdoor adventure |
outdoor_adventure |
Adventure blog |
| Pet together |
pet_together |
Pet lover profile |
| Reading |
reading |
Book club, literary |
| Night city |
night_city |
Urban lifestyle |
| Hanfu (Chinese traditional) |
hanfu |
Cultural, cosplay |
O: Digital avatar
| Style |
Key |
Best for |
| 3D cartoon |
avatar_3d |
Social avatar, Pixar style |
| Gaming avatar |
avatar_gaming |
Game profile, RPG |
| VTuber |
avatar_vtuber |
Streaming, VTuber |
T: Children & family
| Style |
Key |
Best for |
| Child portrait |
child_portrait |
Family keepsake |
| Family photo |
family_photo |
Family portrait |
U: ID / passport photos
| Style |
Key |
Best for |
| ID photo (white bg) |
id_photo_white |
Passport, driver's license |
| ID photo (blue bg) |
id_photo_blue |
Visa, work permit |
6. Model selection guide
| Model |
Key |
Speed |
Quality |
Best for |
| Nano Banana 2 |
nano2 |
~15s |
Good |
Quick drafts, fast iteration, bulk generation. |
| NanoPro |
nanopro |
~25s |
Better |
Default for all requests. Balanced speed and quality. |
| GPT Image 2 |
gpt |
~150s |
Best |
When user explicitly asks for "highest quality" or "best quality". Complex scenes. |
Decision rules:
- Default: always use
nanopro unless the user explicitly requests otherwise.
- Use
nano2 when: user wants fastest results, is iterating on styles, generating many images, or says "quick", "draft", "fast".
- Use
gpt when: user says "highest quality", "best quality", "premium", or the scene is very complex with many specific details.
# Default (fast)
result = generate_portrait(image_path="photo.jpg", style="anime")
# High quality (user requested)
result = generate_portrait(image_path="photo.jpg", style="anime", model="gpt")
7. Custom scene examples
# Style + custom scene
result = generate_portrait(
image_path="uploads/my_photo.jpg",
style="professional",
scene="in a modern office with city skyline view",
)
# Custom scene only (defaults to professional style base)
result = generate_portrait(
image_path="uploads/my_photo.jpg",
scene="standing on a beach at sunset, golden hour lighting",
)
# Fully custom prompt (overrides everything)
result = generate_portrait(
image_path="uploads/my_photo.jpg",
prompt="portrait of a person as a medieval knight, full plate armor, castle background, dramatic lighting, oil painting style",
)
# Different aspect ratio
result = generate_portrait(
image_path="uploads/my_photo.jpg",
style="cyberpunk",
aspect_ratio="9:16",
)
# Multiple images
result = generate_portrait(
image_path="uploads/my_photo.jpg",
style="dating_cafe",
count=4,
)
8. Prompt engineering best practices
When the user's request doesn't match any preset style, or when you need to construct a custom prompt, follow these guidelines (derived from reference skills: ai-headshot-generation, ai-avatar-generation, style-transfer, portrait-enhancement, character-design-sheet, avatar-portrait, nano-banana-pro, pet-portrait-generation).
Automatic likeness preservation
When a reference image is provided (edit mode), the script automatically prepends a likeness preservation instruction to every prompt. This ensures the generated portrait preserves the subject's facial identity. You do NOT need to add likeness instructions manually — the script handles it.
Exception: avatar styles (avatar_3d, avatar_gaming, avatar_vtuber) skip the likeness prefix because stylization takes priority over photographic likeness.
The 7-element prompt structure
Every effective portrait prompt should include these elements (from nano-banana-pro skill):
[subject], [outfit/attire], [pose/action], [expression], [background/setting], [lighting], [style/quality modifiers]
Key principles
Likeness vs. style balance (from avatar-portrait skill):
- Too photorealistic = ignores requested style
- Too stylized = loses resemblance to source person
- For stylized portraits: emphasize "stylized but maintains individual features"
- For photorealistic: emphasize "keep facial features recognizable"
Lighting is critical — always specify lighting type:
- Studio: "soft diffused studio lighting", "Rembrandt chiaroscuro lighting"
- Natural: "golden hour warm light", "dappled sunlight through trees"
- Dramatic: "dramatic rim lighting", "volumetric light beams", "neon glow"
- Flat: "even flat lighting with no shadows" (for ID photos)
Background specificity — vague backgrounds produce poor results:
- ❌ "nice background"
- ✅ "blurred modern office with glass windows and city view"
- ✅ "clean neutral gray gradient studio background"
- ✅ "background style should match the character style" (for avatars)
Lens/camera hints — help the model understand framing:
- "85mm lens look, shallow depth of field" (portrait)
- "head and shoulders framing" (headshot)
- "full body, clean white background" (character design)
- "close-up face, portrait orientation" (expression/avatar)
Quality anchors — add style quality references:
- "professional photography quality", "magazine cover quality"
- "National Geographic photography style" (adventure)
- "League of Legends splash art style" (gaming)
- "Pixar and Disney animation style" (3D avatar)
- "Studio Ghibli inspired" (anime)
- "fine art watercolor painting look" (watercolor)
Texture and material — for artistic styles, specify medium:
- "visible impasto brushstrokes, canvas texture" (oil painting)
- "loose expressive watercolor style, soft edges, beautiful color bleeds and washes" (watercolor)
- "natural film grain, Kodak Portra emulation" (vintage)
- "cel-shaded, clean line art, bold outlines" (anime)
- "visible pixels but NOT a pixelated photo filter" (pixel art)
Expression guidance — be specific about mood:
- ❌ "smiling"
- ✅ "warm genuine smile, confident approachable expression"
- ✅ "neutral calm expression with mouth closed" (ID photo)
- ✅ "passionate expression, energetic" (musician)
Example: building a custom prompt
User: "I want a photo of me as a wizard in a magical forest"
result = generate_portrait(
image_path="uploads/photo.jpg",
prompt=(
"fantasy wizard portrait, wearing mystical purple robes with glowing runes, "
"ancient wooden staff with crystal orb, wise powerful expression, "
"enchanted forest background with bioluminescent plants and floating particles, "
"dramatic magical lighting with ethereal glow, "
"high fantasy art style, detailed digital painting quality"
),
)
# Note: likeness prefix is auto-added because image_path is provided
Example: pixel art avatar (from avatar-portrait skill)
User: "Make me a retro pixel art avatar"
result = generate_portrait(
image_path="uploads/photo.jpg",
prompt=(
"retro 16-bit pixel art portrait, visible pixels with clean lines, "
"rich colors, consistent shading, stylized but maintains individual features, "
"warm sunset cityscape background in matching pixel art style, "
"head and shoulders, square format"
),
)
9. Photo series
Generate a coordinated set of themed portraits in one call. Pass a custom list of styles/scenes — the agent assembles the list based on the user's request.
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_series(
image_path="uploads/my_photo.jpg",
series=[
{"style": "professional"},
{"style": "casual", "scene": "at a rooftop bar, sunset"},
{"style": "anime"},
{"prompt": "portrait as a superhero, cape flowing, city skyline"},
],
)
# result -> {"success": True, "images": [...4 images...], "series": "custom"}
Each item in the list is a dict with optional keys:
style — any style key from §7 (e.g. "professional", "anime", "cyberpunk")
scene — override the scene description (combined with the style template)
prompt — fully custom prompt (ignores style/scene)
10. Intent recognition guide
Use this table to map user requests to the correct style/parameters:
| User says |
Style |
Notes |
| "professional photo", "headshot", "LinkedIn photo" |
professional or linkedin |
|
| "dating photo", "dating app", "Tinder photo" |
dating_cafe / dating_beach / dating_city |
Ask which vibe |
| "anime me", "anime version", "cartoon me" |
anime |
|
| "cyberpunk", "sci-fi portrait" |
cyberpunk |
|
| "oil painting", "classical portrait" |
oil_painting |
|
| "watercolor portrait" |
watercolor |
|
| "vintage photo", "retro" |
vintage |
|
| "casual photo", "lifestyle" |
casual |
|
| "travel photo in Paris/Europe" |
travel_europe |
|
| "travel photo in Japan/Tokyo/Kyoto" |
travel_japan |
|
| "beach photo", "tropical" |
travel_tropical or dating_beach |
|
| "gym photo", "fitness" |
sports_gym |
|
| "Christmas photo" |
christmas |
|
| "Halloween photo" |
halloween |
|
| "graduation photo" |
graduation |
|
| "wedding photo" |
wedding |
|
| "chef photo", "cooking" |
chef |
|
| "musician", "on stage" |
musician |
|
| "with my dog/pet" |
pet_together |
|
| "reading", "bookish" |
reading |
|
| "night city", "urban night" |
night_city |
|
| "hanfu", "Chinese traditional" |
hanfu |
|
| "3D avatar", "Pixar style" |
avatar_3d |
|
| "gaming avatar", "RPG character" |
avatar_gaming |
|
| "VTuber avatar" |
avatar_vtuber |
|
| "kid photo", "children's portrait" |
child_portrait |
|
| "family photo" |
family_photo |
|
| "passport photo", "ID photo" |
id_photo_white |
White bg default |
| "visa photo" |
id_photo_blue |
Blue bg |
| "photo series", "set of photos" |
Use generate_series() |
Assemble custom list from styles |
| "highest quality", "best quality" |
Any style + model="gpt" |
|
| Custom scene not in presets |
Use scene= or prompt= |
|
When NOT to use this skill (routing)
This skill's core contract is identity preservation: whenever a reference photo is provided, a likeness prefix ("preserve the subject's exact facial features…") is prepended to every prompt (except the 3 avatar_* styles). This means:
- User wants to drastically change the face/identity or fully re-imagine the person (e.g. "make me look like a different person", heavy character redesign) → route to image-create (text-to-image) instead. The likeness prefix will fight the stylization and iterations won't converge.
- User wants strong stylization but still recognizable → stay here; use
anime / avatar_3d etc.
- User wants to edit a non-person photo → image-edit.
If a request keeps failing to move away from the reference photo's look after 2+ iterations, that's the likeness contract working as designed — switch skills rather than re-prompting.
11. Provided scripts
| File |
Purpose |
generate_portrait.py |
Core script: submit → poll → download. Handles local files (base64) and URLs, all styles, custom scenes, three models (nano2/nanopro/gpt). |
exports.py |
Re-exports generate_portrait, generate_series, STYLE_PROMPTS for programmatic use by other skills. |
_cost_track.py |
Cost tracking helper — records per-call costs via sc-proxy headers. |
12. Local testing
Set FAL_KEY env var to call fal.ai directly (bypasses sc-proxy):
# Single portrait
FAL_KEY=your-fal-key python3 skills/image-portrait/generate_portrait.py photo.jpg anime 1 nanopro
# Args: <image_path_or_url> [style] [count] [model]
13. Troubleshooting
| Problem |
Fix |
File not found: ... |
Check the workspace path; the file must exist |
Unsupported image format |
Use .jpg, .jpeg, .png, .webp, or .bmp |
Image too large |
Resize to under 10 MB before uploading |
face_image_url must be a public HTTP(S) URL |
Use image_path for local files, or provide a valid https:// URL |
HTTP 402 insufficient_credits |
Top up balance; cost is pre-charged on submit |
HTTP 403 endpoint_not_allowed |
sc-proxy only allows approved fal endpoints; contact admin |
Generation FAILED upstream |
Simplify prompt, ensure face photo is clear and well-lit, retry |
Job stuck IN_PROGRESS >10 min |
Save request_id, retry later |
| Poor face consistency |
Use a clear, front-facing photo with good lighting; avoid group photos |
gpt model too slow |
Switch to nanopro (default) for faster results |
14. Infrastructure (reference)
- Caller →
sc-proxy → queue.fal.run/{model} → fal model providers
- All requests must include
Authorization: Key fake-falai-key-12345 (proxy injects the real FAL_KEY)
- Pre-charge happens at submit. Poll/result calls are free.
- Local files are base64-encoded as data URIs — no separate upload step needed.
- Final images live at
https://*.fal.media/... — public CDN, no auth needed for download.
- Cost tracking via
_cost_track.py — records X-Credits-Used from sc-proxy response headers.
Model endpoints
| Model |
Edit (with ref image) |
Generate (text only) |
| nano2 |
fal-ai/nano-banana-2/edit |
fal-ai/nano-banana-2 |
| nanopro |
fal-ai/nano-banana-pro/edit |
fal-ai/nano-banana-pro |
| gpt |
openai/gpt-image-2/edit |
openai/gpt-image-2 |
1---2name: image-portrait3description: Identity-consistent portrait generation from a reference photo. Covers professional headshots, dating photos, style transfers, themed portraits, photo series, avatars, ID photos. Use when generating styled portraits from a reference photo (e.g. professional headshot, anime avatar, cyberpunk portrait, travel photo, dating profile photo, ID photo).4---56# image-portrait78Use this skill for **all identity-consistent portrait generation requests** on Starchild.910Covers: professional headshots, dating/social photos, artistic style transfers, themed/holiday portraits, photo series, digital avatars, children/family photos, ID/passport photos.1112**Core principle:** call the provided script. Do not re-implement proxy/billing plumbing.1314---1516## 1. Quick start — single portrait (most common)1718> **⚠️ Execution context — read this first.**19> The code blocks below are **Python**, not shell commands. Starchild's `bash` tool20> runs `/bin/bash -c`, which cannot parse `exec(open(...))` — pasting them directly21> into a bash command will fail with `syntax error near unexpected token 'open'`.22> Also, `exec(open(...))` inside `python3 -c` fails with `NameError: __file__`23> because the script uses `__file__` for path resolution.24>25> **Use `python3 - <<'EOF'` with `from exports import` when calling via the bash tool:**26>27> ```bash28> python3 - <<'EOF'29> import sys30> sys.path.insert(0, "skills/image-portrait")31> from exports import generate_portrait32> result = generate_portrait(33> image_path="path/to/user/photo.jpg",34> style="professional",35> )36> print(result)37> EOF38> ```39>40> The heredoc (`<<'EOF'`) preserves all quotes and newlines — no escaping needed.4142```python43exec(open('skills/image-portrait/generate_portrait.py').read())44result = generate_portrait(45 image_path="path/to/user/photo.jpg",46 style="professional",47)48# result -> {"success": True, "images": [{"local_path": "output/images/..."}], ...}49```5051The script reads the local file, base64-encodes it, and sends it to fal.ai as a data URI — no manual URL publishing needed.5253## 2. Quick start — public URL5455```python56exec(open('skills/image-portrait/generate_portrait.py').read())57result = generate_portrait(58 face_image_url="https://example.com/photo.jpg",59 style="anime",60)61```6263## 3. Quick start — text-to-image (no reference photo)6465```python66exec(open('skills/image-portrait/generate_portrait.py').read())67result = generate_portrait(68 prompt="a young woman in cyberpunk armor, neon city background, rain",69 model="nanopro",70)71```7273When no `image_path` or `face_image_url` is provided, the script uses the text-to-image endpoint (no `/edit` suffix).7475### Delivering the result to the user — IMPORTANT7677**Never hand the user the raw fal.media URL.** fal serves files with restrictive CSP headers. The only reliable delivery path is the **already-downloaded local file**:78791. Use each image's `local_path` (e.g. `output/images/xxx.png`) — the script always downloads on success.802. Tell the user the files are saved to `output/images/` and viewable in the workspace file panel.813. On Web channel, embed inline so the user can preview in chat:82 ```markdown83 84 ```854. On Telegram / WeChat: send via `send_to_telegram(file_path="output/images/...", message_type="image")` or `send_to_wechat(file_path="output/images/...", message_type="image")`.8687---8889## 4. Parameters9091| Parameter | Required | Default | Description |92|-----------|----------|---------|-------------|93| `image_path` | no | — | Local workspace file path to the user's face photo |94| `face_image_url` | no | — | Public HTTPS URL of the user's face photo |95| `style` | no | `"professional"` | Preset style key (see §5) |96| `scene` | no | `None` | Custom scene description (appended to style prompt) |97| `prompt` | no | `None` | Fully custom prompt — overrides style+scene when set |98| `model` | no | `"nanopro"` | Model: `"nano2"` (fastest ~15s), `"nanopro"` (balanced ~25s, default), or `"gpt"` (best quality ~150s) |99| `count` | no | `1` | Number of images to generate (1–8) |100| `aspect_ratio` | no | `"1:1"` | Output ratio: `1:1`, `3:4`, `4:3`, `9:16`, `16:9` |101102**Image input rules:**103- Provide `image_path` OR `face_image_url` for identity-consistent generation (edit mode).104- If both are given, `image_path` takes priority.105- Omit both for pure text-to-image generation (generate mode).106107**Prompt priority:** `prompt` > `style + scene` > `style` > default (`professional`).108109---110111## 5. Style presets112113### A: Identity-consistent character styles114115| Style | Key | Best for |116|-------|-----|----------|117| Professional headshot | `professional` | LinkedIn, resume, corporate |118| Artistic portrait | `artistic` | Creative portfolio, gallery |119| Anime | `anime` | Social media, fun avatar |120| Cyberpunk | `cyberpunk` | Gaming profile, sci-fi fan |121| Oil painting | `oil_painting` | Art gift, classical look |122| Watercolor | `watercolor` | Soft artistic portrait |123| Vintage | `vintage` | Retro aesthetic, nostalgia |124| Casual lifestyle | `casual` | Social media, personal blog |125126### B: Personal showcase / dating / social127128| Style | Key | Best for |129|-------|-----|----------|130| Dating — cafe | `dating_cafe` | Dating app, warm vibe |131| Dating — beach | `dating_beach` | Dating app, summer vibe |132| Dating — city | `dating_city` | Dating app, urban vibe |133| Dating — restaurant | `dating_restaurant` | Dating app, elegant vibe |134| Travel — Europe | `travel_europe` | Travel blog, social media |135| Travel — Japan | `travel_japan` | Travel blog, cultural |136| Travel — tropical | `travel_tropical` | Vacation, resort |137| Sports — gym | `sports_gym` | Fitness profile |138| Sports — running | `sports_running` | Athletic profile |139| Social media | `social_media` | Instagram, TikTok |140| LinkedIn | `linkedin` | Professional networking |141| Personal brand | `personal_brand` | Entrepreneur, creator |142143### D: Themed / scene portraits144145| Style | Key | Best for |146|-------|-----|----------|147| Christmas | `christmas` | Holiday greeting, social |148| Halloween | `halloween` | Holiday fun |149| Graduation | `graduation` | Milestone celebration |150| Wedding | `wedding` | Wedding planning, save-the-date |151| Business speech | `business_speech` | Speaker profile |152| Musician | `musician` | Music promotion |153| Chef | `chef` | Food blog, restaurant |154| Outdoor adventure | `outdoor_adventure` | Adventure blog |155| Pet together | `pet_together` | Pet lover profile |156| Reading | `reading` | Book club, literary |157| Night city | `night_city` | Urban lifestyle |158| Hanfu (Chinese traditional) | `hanfu` | Cultural, cosplay |159160### O: Digital avatar161162| Style | Key | Best for |163|-------|-----|----------|164| 3D cartoon | `avatar_3d` | Social avatar, Pixar style |165| Gaming avatar | `avatar_gaming` | Game profile, RPG |166| VTuber | `avatar_vtuber` | Streaming, VTuber |167168### T: Children & family169170| Style | Key | Best for |171|-------|-----|----------|172| Child portrait | `child_portrait` | Family keepsake |173| Family photo | `family_photo` | Family portrait |174175### U: ID / passport photos176177| Style | Key | Best for |178|-------|-----|----------|179| ID photo (white bg) | `id_photo_white` | Passport, driver's license |180| ID photo (blue bg) | `id_photo_blue` | Visa, work permit |181182---183184## 6. Model selection guide185186| Model | Key | Speed | Quality | Best for |187|-------|-----|-------|---------|----------|188| Nano Banana 2 | `nano2` | ~15s | Good | Quick drafts, fast iteration, bulk generation. |189| NanoPro | `nanopro` | ~25s | Better | Default for all requests. Balanced speed and quality. |190| GPT Image 2 | `gpt` | ~150s | Best | When user explicitly asks for "highest quality" or "best quality". Complex scenes. |191192**Decision rules:**1931. **Default:** always use `nanopro` unless the user explicitly requests otherwise.1942. **Use `nano2` when:** user wants fastest results, is iterating on styles, generating many images, or says "quick", "draft", "fast".1953. **Use `gpt` when:** user says "highest quality", "best quality", "premium", or the scene is very complex with many specific details.196197```python198# Default (fast)199result = generate_portrait(image_path="photo.jpg", style="anime")200201# High quality (user requested)202result = generate_portrait(image_path="photo.jpg", style="anime", model="gpt")203```204205---206207## 7. Custom scene examples208209```python210# Style + custom scene211result = generate_portrait(212 image_path="uploads/my_photo.jpg",213 style="professional",214 scene="in a modern office with city skyline view",215)216217# Custom scene only (defaults to professional style base)218result = generate_portrait(219 image_path="uploads/my_photo.jpg",220 scene="standing on a beach at sunset, golden hour lighting",221)222223# Fully custom prompt (overrides everything)224result = generate_portrait(225 image_path="uploads/my_photo.jpg",226 prompt="portrait of a person as a medieval knight, full plate armor, castle background, dramatic lighting, oil painting style",227)228229# Different aspect ratio230result = generate_portrait(231 image_path="uploads/my_photo.jpg",232 style="cyberpunk",233 aspect_ratio="9:16",234)235236# Multiple images237result = generate_portrait(238 image_path="uploads/my_photo.jpg",239 style="dating_cafe",240 count=4,241)242```243244---245246## 8. Prompt engineering best practices247248When the user's request doesn't match any preset style, or when you need to construct a custom `prompt`, follow these guidelines (derived from reference skills: ai-headshot-generation, ai-avatar-generation, style-transfer, portrait-enhancement, character-design-sheet, avatar-portrait, nano-banana-pro, pet-portrait-generation).249250### Automatic likeness preservation251252When a reference image is provided (edit mode), the script **automatically prepends** a likeness preservation instruction to every prompt. This ensures the generated portrait preserves the subject's facial identity. You do NOT need to add likeness instructions manually — the script handles it.253254Exception: avatar styles (`avatar_3d`, `avatar_gaming`, `avatar_vtuber`) skip the likeness prefix because stylization takes priority over photographic likeness.255256### The 7-element prompt structure257258Every effective portrait prompt should include these elements (from nano-banana-pro skill):259260```261[subject], [outfit/attire], [pose/action], [expression], [background/setting], [lighting], [style/quality modifiers]262```263264### Key principles2652661. **Likeness vs. style balance** (from avatar-portrait skill):267 - Too photorealistic = ignores requested style268 - Too stylized = loses resemblance to source person269 - For stylized portraits: emphasize "stylized but maintains individual features"270 - For photorealistic: emphasize "keep facial features recognizable"2712722. **Lighting is critical** — always specify lighting type:273 - Studio: "soft diffused studio lighting", "Rembrandt chiaroscuro lighting"274 - Natural: "golden hour warm light", "dappled sunlight through trees"275 - Dramatic: "dramatic rim lighting", "volumetric light beams", "neon glow"276 - Flat: "even flat lighting with no shadows" (for ID photos)2772783. **Background specificity** — vague backgrounds produce poor results:279 - ❌ "nice background"280 - ✅ "blurred modern office with glass windows and city view"281 - ✅ "clean neutral gray gradient studio background"282 - ✅ "background style should match the character style" (for avatars)2832844. **Lens/camera hints** — help the model understand framing:285 - "85mm lens look, shallow depth of field" (portrait)286 - "head and shoulders framing" (headshot)287 - "full body, clean white background" (character design)288 - "close-up face, portrait orientation" (expression/avatar)2892905. **Quality anchors** — add style quality references:291 - "professional photography quality", "magazine cover quality"292 - "National Geographic photography style" (adventure)293 - "League of Legends splash art style" (gaming)294 - "Pixar and Disney animation style" (3D avatar)295 - "Studio Ghibli inspired" (anime)296 - "fine art watercolor painting look" (watercolor)2972986. **Texture and material** — for artistic styles, specify medium:299 - "visible impasto brushstrokes, canvas texture" (oil painting)300 - "loose expressive watercolor style, soft edges, beautiful color bleeds and washes" (watercolor)301 - "natural film grain, Kodak Portra emulation" (vintage)302 - "cel-shaded, clean line art, bold outlines" (anime)303 - "visible pixels but NOT a pixelated photo filter" (pixel art)3043057. **Expression guidance** — be specific about mood:306 - ❌ "smiling"307 - ✅ "warm genuine smile, confident approachable expression"308 - ✅ "neutral calm expression with mouth closed" (ID photo)309 - ✅ "passionate expression, energetic" (musician)310311### Example: building a custom prompt312313User: "I want a photo of me as a wizard in a magical forest"314315```python316result = generate_portrait(317 image_path="uploads/photo.jpg",318 prompt=(319 "fantasy wizard portrait, wearing mystical purple robes with glowing runes, "320 "ancient wooden staff with crystal orb, wise powerful expression, "321 "enchanted forest background with bioluminescent plants and floating particles, "322 "dramatic magical lighting with ethereal glow, "323 "high fantasy art style, detailed digital painting quality"324 ),325)326# Note: likeness prefix is auto-added because image_path is provided327```328329### Example: pixel art avatar (from avatar-portrait skill)330331User: "Make me a retro pixel art avatar"332333```python334result = generate_portrait(335 image_path="uploads/photo.jpg",336 prompt=(337 "retro 16-bit pixel art portrait, visible pixels with clean lines, "338 "rich colors, consistent shading, stylized but maintains individual features, "339 "warm sunset cityscape background in matching pixel art style, "340 "head and shoulders, square format"341 ),342)343```344345---346347## 9. Photo series348349Generate a coordinated set of themed portraits in one call. Pass a custom list of styles/scenes — the agent assembles the list based on the user's request.350351```python352exec(open('skills/image-portrait/generate_portrait.py').read())353result = generate_series(354 image_path="uploads/my_photo.jpg",355 series=[356 {"style": "professional"},357 {"style": "casual", "scene": "at a rooftop bar, sunset"},358 {"style": "anime"},359 {"prompt": "portrait as a superhero, cape flowing, city skyline"},360 ],361)362# result -> {"success": True, "images": [...4 images...], "series": "custom"}363```364365Each item in the list is a dict with optional keys:366- `style` — any style key from §7 (e.g. `"professional"`, `"anime"`, `"cyberpunk"`)367- `scene` — override the scene description (combined with the style template)368- `prompt` — fully custom prompt (ignores style/scene)369370---371372## 10. Intent recognition guide373374Use this table to map user requests to the correct style/parameters:375376| User says | Style | Notes |377|-----------|-------|-------|378| "professional photo", "headshot", "LinkedIn photo" | `professional` or `linkedin` | |379| "dating photo", "dating app", "Tinder photo" | `dating_cafe` / `dating_beach` / `dating_city` | Ask which vibe |380| "anime me", "anime version", "cartoon me" | `anime` | |381| "cyberpunk", "sci-fi portrait" | `cyberpunk` | |382| "oil painting", "classical portrait" | `oil_painting` | |383| "watercolor portrait" | `watercolor` | |384| "vintage photo", "retro" | `vintage` | |385| "casual photo", "lifestyle" | `casual` | |386| "travel photo in Paris/Europe" | `travel_europe` | |387| "travel photo in Japan/Tokyo/Kyoto" | `travel_japan` | |388| "beach photo", "tropical" | `travel_tropical` or `dating_beach` | |389| "gym photo", "fitness" | `sports_gym` | |390| "Christmas photo" | `christmas` | |391| "Halloween photo" | `halloween` | |392| "graduation photo" | `graduation` | |393| "wedding photo" | `wedding` | |394| "chef photo", "cooking" | `chef` | |395| "musician", "on stage" | `musician` | |396| "with my dog/pet" | `pet_together` | |397| "reading", "bookish" | `reading` | |398| "night city", "urban night" | `night_city` | |399| "hanfu", "Chinese traditional" | `hanfu` | |400| "3D avatar", "Pixar style" | `avatar_3d` | |401| "gaming avatar", "RPG character" | `avatar_gaming` | |402| "VTuber avatar" | `avatar_vtuber` | |403| "kid photo", "children's portrait" | `child_portrait` | |404| "family photo" | `family_photo` | |405| "passport photo", "ID photo" | `id_photo_white` | White bg default |406| "visa photo" | `id_photo_blue` | Blue bg |407| "photo series", "set of photos" | Use `generate_series()` | Assemble custom list from styles |408| "highest quality", "best quality" | Any style + `model="gpt"` | |409| Custom scene not in presets | Use `scene=` or `prompt=` | |410411### When NOT to use this skill (routing)412413This skill's core contract is **identity preservation**: whenever a reference photo is provided, a likeness prefix ("preserve the subject's exact facial features…") is prepended to every prompt (except the 3 `avatar_*` styles). This means:414415- **User wants to drastically change the face/identity or fully re-imagine the person** (e.g. "make me look like a different person", heavy character redesign) → route to **image-create** (text-to-image) instead. The likeness prefix will fight the stylization and iterations won't converge.416- **User wants strong stylization but still recognizable** → stay here; use `anime` / `avatar_3d` etc.417- **User wants to edit a non-person photo** → **image-edit**.418419If a request keeps failing to move away from the reference photo's look after 2+ iterations, that's the likeness contract working as designed — switch skills rather than re-prompting.420421---422423## 11. Provided scripts424425| File | Purpose |426|------|---------|427| `generate_portrait.py` | Core script: submit → poll → download. Handles local files (base64) and URLs, all styles, custom scenes, three models (nano2/nanopro/gpt). |428| `exports.py` | Re-exports `generate_portrait`, `generate_series`, `STYLE_PROMPTS` for programmatic use by other skills. |429| `_cost_track.py` | Cost tracking helper — records per-call costs via sc-proxy headers. |430431---432433## 12. Local testing434435Set `FAL_KEY` env var to call fal.ai directly (bypasses sc-proxy):436437```bash438# Single portrait439FAL_KEY=your-fal-key python3 skills/image-portrait/generate_portrait.py photo.jpg anime 1 nanopro440441# Args: <image_path_or_url> [style] [count] [model]442```443444---445446## 13. Troubleshooting447448| Problem | Fix |449|---------|-----|450| `File not found: ...` | Check the workspace path; the file must exist |451| `Unsupported image format` | Use `.jpg`, `.jpeg`, `.png`, `.webp`, or `.bmp` |452| `Image too large` | Resize to under 10 MB before uploading |453| `face_image_url must be a public HTTP(S) URL` | Use `image_path` for local files, or provide a valid `https://` URL |454| `HTTP 402 insufficient_credits` | Top up balance; cost is pre-charged on submit |455| `HTTP 403 endpoint_not_allowed` | sc-proxy only allows approved fal endpoints; contact admin |456| Generation `FAILED` upstream | Simplify prompt, ensure face photo is clear and well-lit, retry |457| Job stuck `IN_PROGRESS` >10 min | Save `request_id`, retry later |458| Poor face consistency | Use a clear, front-facing photo with good lighting; avoid group photos |459| `gpt` model too slow | Switch to `nanopro` (default) for faster results |460461---462463## 14. Infrastructure (reference)464465- Caller → `sc-proxy` → `queue.fal.run/{model}` → fal model providers466- All requests must include `Authorization: Key fake-falai-key-12345` (proxy injects the real `FAL_KEY`)467- Pre-charge happens at submit. Poll/result calls are free.468- Local files are base64-encoded as data URIs — no separate upload step needed.469- Final images live at `https://*.fal.media/...` — public CDN, no auth needed for download.470- Cost tracking via `_cost_track.py` — records `X-Credits-Used` from sc-proxy response headers.471472### Model endpoints473474| Model | Edit (with ref image) | Generate (text only) |475|-------|----------------------|---------------------|476| nano2 | `fal-ai/nano-banana-2/edit` | `fal-ai/nano-banana-2` |477| nanopro | `fal-ai/nano-banana-pro/edit` | `fal-ai/nano-banana-pro` |478| gpt | `openai/gpt-image-2/edit` | `openai/gpt-image-2` |479480---