Spaces:
Running
Running
Upload 3 files
Browse files- models/llm.py +126 -0
- models/test_llm.py +162 -0
- models/vision.py +50 -0
models/llm.py
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
from transformers import pipeline
|
3 |
+
|
4 |
+
class StyleSavvy:
|
5 |
+
def __init__(
|
6 |
+
self,
|
7 |
+
model_name: str = "google/flan-t5-large",
|
8 |
+
device: int = -1, # -1 = CPU, or GPU index
|
9 |
+
max_length: int = 150,
|
10 |
+
):
|
11 |
+
# A local instruction-tuned T5 model
|
12 |
+
self.pipe = pipeline(
|
13 |
+
"text2text-generation",
|
14 |
+
model=model_name,
|
15 |
+
tokenizer=model_name,
|
16 |
+
device=device,
|
17 |
+
)
|
18 |
+
self.max_length = max_length
|
19 |
+
# TODO: Modification: Add more prompts to the advise function
|
20 |
+
# to make it more specific to the user's needs.
|
21 |
+
# The function now takes in the user's body type, face shape, and occasion
|
22 |
+
# and generates style tips accordingly.
|
23 |
+
|
24 |
+
def advise(self, items, body_type, face_shape, occasion):
|
25 |
+
prompt = (
|
26 |
+
f"The user is {body_type}-shaped with a {face_shape} face, "
|
27 |
+
f"attending a {occasion}. They are wearing: "
|
28 |
+
+ ", ".join(i["label"] for i in items)
|
29 |
+
+ ".\n\nPlease list 5 concise style tips as bullet points:"
|
30 |
+
)
|
31 |
+
|
32 |
+
# Generate with supported args only
|
33 |
+
result = self.pipe(
|
34 |
+
prompt,
|
35 |
+
max_length=self.max_length,
|
36 |
+
num_beams=4,
|
37 |
+
early_stopping=True,
|
38 |
+
do_sample=False
|
39 |
+
)[0]["generated_text"].strip()
|
40 |
+
|
41 |
+
return result
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
# import torch
|
47 |
+
|
48 |
+
# # models/llm.py
|
49 |
+
|
50 |
+
# # models/llm.py
|
51 |
+
|
52 |
+
# import os
|
53 |
+
# from typing import List
|
54 |
+
# from transformers import pipeline, Pipeline
|
55 |
+
|
56 |
+
# # Force CPU modes (avoid any MPS/CUDA issues on macOS)
|
57 |
+
# os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
58 |
+
# os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "0"
|
59 |
+
|
60 |
+
# class StyleSavvy:
|
61 |
+
# def __init__(
|
62 |
+
# self,
|
63 |
+
# model_name: str = "openlm-research/open_llama_3b_v2",
|
64 |
+
# device: int = -1, # -1 = CPU
|
65 |
+
# max_new_tokens: int = 100,
|
66 |
+
# temperature: float = 0.7,
|
67 |
+
# top_p: float = 0.9,
|
68 |
+
# ):
|
69 |
+
# """
|
70 |
+
# Uses OpenLLaMA-3B-v2 (≈3B params) for fast, local inference.
|
71 |
+
# """
|
72 |
+
# # Setup a causal text-generation pipeline
|
73 |
+
# self.pipe: Pipeline = pipeline(
|
74 |
+
# "text-generation",
|
75 |
+
# model=model_name,
|
76 |
+
# tokenizer=model_name,
|
77 |
+
# device=device,
|
78 |
+
# )
|
79 |
+
# # GPT‐style models need a pad token to avoid warnings
|
80 |
+
# if self.pipe.tokenizer.pad_token_id is None:
|
81 |
+
# self.pipe.tokenizer.pad_token = self.pipe.tokenizer.eos_token
|
82 |
+
|
83 |
+
# self.max_new_tokens = max_new_tokens
|
84 |
+
# self.temperature = temperature
|
85 |
+
# self.top_p = top_p
|
86 |
+
|
87 |
+
# def advise(
|
88 |
+
# self,
|
89 |
+
# items: List[str],
|
90 |
+
# body_type: str,
|
91 |
+
# face_shape: str,
|
92 |
+
# occasion: str
|
93 |
+
# ) -> List[str]:
|
94 |
+
# """
|
95 |
+
# Builds a strict instruction prompt and returns exactly five "- " bullets.
|
96 |
+
# """
|
97 |
+
# labels = ", ".join(items) if items else "an outfit"
|
98 |
+
# prompt = (
|
99 |
+
# "You are a professional fashion consultant.\n"
|
100 |
+
# f"The user is {body_type}-shaped with a {face_shape} face, attending {occasion}.\n"
|
101 |
+
# f"They are wearing: {labels}.\n\n"
|
102 |
+
# "Please provide exactly five concise style tips, each on its own line, "
|
103 |
+
# "and starting with \"- \". No extra text."
|
104 |
+
# )
|
105 |
+
|
106 |
+
# # Generate
|
107 |
+
# output = self.pipe(
|
108 |
+
# prompt,
|
109 |
+
# max_new_tokens=self.max_new_tokens,
|
110 |
+
# do_sample=True,
|
111 |
+
# temperature=self.temperature,
|
112 |
+
# top_p=self.top_p,
|
113 |
+
# return_full_text=False,
|
114 |
+
# )[0]["generated_text"]
|
115 |
+
|
116 |
+
# # Extract bullets
|
117 |
+
# tips = [ln.strip() for ln in output.splitlines() if ln.strip().startswith("- ")]
|
118 |
+
# # Fallback: split on sentences if fewer than 5 bullets
|
119 |
+
# if len(tips) < 5:
|
120 |
+
# candidates = [s.strip() for s in output.replace("\n"," ").split(".") if s.strip()]
|
121 |
+
# tips = [f"- {candidates[i]}" for i in range(min(5, len(candidates)))]
|
122 |
+
|
123 |
+
# return tips[:5]
|
124 |
+
|
125 |
+
|
126 |
+
|
models/test_llm.py
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# # test_llm.py
|
2 |
+
# """
|
3 |
+
# Test harness for StyleSavvy LLM prompts.
|
4 |
+
# Defines multiple prompt templates and evaluates the generated outputs,
|
5 |
+
# checking for the expected number of bullet-point style tips.
|
6 |
+
# """
|
7 |
+
# from models.llm import StyleSavvy
|
8 |
+
|
9 |
+
# # Variant prompt templates with placeholders
|
10 |
+
# PROMPT_TEMPLATES = {
|
11 |
+
# "occasion_driven": (
|
12 |
+
# "You are an expert fashion stylist. A client is preparing for {occasion}. "
|
13 |
+
# "They have a {body_type}-shaped body and a {face_shape} face. They’re currently wearing: {items}. "
|
14 |
+
# "Give 3 to 5 *distinct* style tips focused on making them look their best at the event. "
|
15 |
+
# "Make the suggestions relevant to the setting, weather, and formality of the occasion. "
|
16 |
+
# "Avoid repeating any advice."
|
17 |
+
# ),
|
18 |
+
|
19 |
+
# "function_based": (
|
20 |
+
# "You're advising someone with a {body_type} build and {face_shape} face. "
|
21 |
+
# "They're attending a {occasion} and are wearing {items}. "
|
22 |
+
# "Suggest 3–5 concise fashion improvements or enhancements. "
|
23 |
+
# "Each suggestion should be unique and tailored to the event. "
|
24 |
+
# "Include practical choices for color, layering, accessories, or footwear. "
|
25 |
+
# "Avoid repeating words or phrases."
|
26 |
+
# ),
|
27 |
+
|
28 |
+
# "intent_style": (
|
29 |
+
# "Act as a high-end personal stylist. Your client has a {body_type} body shape and a {face_shape} face. "
|
30 |
+
# "They're going to a {occasion} and are wearing {items}. "
|
31 |
+
# "Write 3 to 5 brief but powerful styling suggestions to elevate their look. "
|
32 |
+
# "Focus on intent—what feeling or impression each style choice creates for the event."
|
33 |
+
# ),
|
34 |
+
# }
|
35 |
+
|
36 |
+
|
37 |
+
# # Test parameters
|
38 |
+
# BODY_TYPE = "Slim"
|
39 |
+
# FACE_SHAPE = "Round"
|
40 |
+
# OCCASION = "Rooftop Evening Party"
|
41 |
+
# ITEMS = ["shirt", "jeans", "jacket","shoes"]
|
42 |
+
|
43 |
+
# if __name__ == "__main__":
|
44 |
+
# advisor = StyleSavvy()
|
45 |
+
|
46 |
+
# for name, template in PROMPT_TEMPLATES.items():
|
47 |
+
# # Build prompt by replacing placeholders
|
48 |
+
# prompt = template.format(
|
49 |
+
# body_type=BODY_TYPE,
|
50 |
+
# face_shape=FACE_SHAPE,
|
51 |
+
# occasion=OCCASION,
|
52 |
+
# items=", ".join(ITEMS)
|
53 |
+
# )
|
54 |
+
# print(f"=== Testing template: {name} ===")
|
55 |
+
# print("Prompt:")
|
56 |
+
# print(prompt)
|
57 |
+
|
58 |
+
# # Generate output (use only supported args)
|
59 |
+
# result = advisor.pipe(
|
60 |
+
# prompt,
|
61 |
+
# max_length=advisor.max_length,
|
62 |
+
# early_stopping=True,
|
63 |
+
# do_sample=False
|
64 |
+
# )[0]["generated_text"].strip()
|
65 |
+
|
66 |
+
# print("Generated output:")
|
67 |
+
# print(result)
|
68 |
+
|
69 |
+
# # Extract bullet lines
|
70 |
+
# bullets = [ln for ln in result.splitlines() if ln.strip().startswith("- ")]
|
71 |
+
# print(f"Number of bullets detected: {len(bullets)}")
|
72 |
+
# for i, b in enumerate(bullets, start=1):
|
73 |
+
# print(f" {i}. {b}")
|
74 |
+
# print("" + "-"*40)
|
75 |
+
|
76 |
+
|
77 |
+
# test_llm.py
|
78 |
+
"""
|
79 |
+
Test harness for StyleSavvy LLM prompts.
|
80 |
+
Evaluates multiple prompt templates and parses the generated outputs into distinct tips.
|
81 |
+
"""
|
82 |
+
|
83 |
+
from models.llm import StyleSavvy
|
84 |
+
|
85 |
+
# Variant prompt templates with placeholders
|
86 |
+
PROMPT_TEMPLATES = {
|
87 |
+
"direct_instruction": (
|
88 |
+
"You are a professional fashion stylist. A client with a {body_type} body shape "
|
89 |
+
"and {face_shape} face is preparing for a {occasion}. They are currently wearing {items}. "
|
90 |
+
"Give exactly five different styling tips to improve their outfit. "
|
91 |
+
"Each tip should be concise, actionable, and relevant to the event. Start each tip on a new line."
|
92 |
+
),
|
93 |
+
"category_expansion": (
|
94 |
+
"As a high-end fashion advisor, provide five styling tips for a {body_type}-shaped person "
|
95 |
+
"with a {face_shape} face attending a {occasion}. They are currently wearing {items}. "
|
96 |
+
"Offer one tip for each of the following categories: silhouette, color, accessories, footwear, and layering. "
|
97 |
+
"Each tip must be brief, specific, and clearly separated by a line break."
|
98 |
+
),
|
99 |
+
"event_aesthetic": (
|
100 |
+
"Imagine you're curating a perfect outfit for a {body_type}-shaped individual with a {face_shape} face "
|
101 |
+
"attending {occasion}. They are wearing {items}. Suggest 5 ways to enhance their style, focusing on event-appropriate aesthetics. "
|
102 |
+
"Write each tip as a separate sentence on a new line. Do not repeat advice or themes."
|
103 |
+
),
|
104 |
+
"fashion_editor": (
|
105 |
+
"As a fashion editor writing for a style magazine, outline five unique styling tips for a {body_type}-shaped reader "
|
106 |
+
"with a {face_shape} face who is attending {occasion}. They currently wear {items}. "
|
107 |
+
"Each recommendation should reflect expertise, relevance to the occasion, and a unique style element. "
|
108 |
+
"Deliver all five tips in a list format, starting each on a new line."
|
109 |
+
),
|
110 |
+
"influencer_style": (
|
111 |
+
"You’re an influencer known for your sharp styling advice. One of your followers has a {body_type} body and "
|
112 |
+
"{face_shape} face, and they're attending {occasion}. They’ve sent you a photo wearing {items}. "
|
113 |
+
"Reply with exactly five snappy, modern style tips they can use to upgrade their outfit for the event. "
|
114 |
+
"Make sure each tip is short, non-repetitive, and on its own line."
|
115 |
+
),
|
116 |
+
}
|
117 |
+
|
118 |
+
|
119 |
+
# Test parameters
|
120 |
+
BODY_TYPE = "Slim"
|
121 |
+
FACE_SHAPE = "Round"
|
122 |
+
OCCASION = "Rooftop Evening Party"
|
123 |
+
ITEMS = ["jeans", "jacket", "shoes"]
|
124 |
+
|
125 |
+
if __name__ == "__main__":
|
126 |
+
advisor = StyleSavvy()
|
127 |
+
|
128 |
+
for name, template in PROMPT_TEMPLATES.items():
|
129 |
+
print(f"=== Testing template: {name} ===")
|
130 |
+
|
131 |
+
# Build prompt
|
132 |
+
prompt = template.format(
|
133 |
+
body_type=BODY_TYPE,
|
134 |
+
face_shape=FACE_SHAPE,
|
135 |
+
occasion=OCCASION,
|
136 |
+
items=", ".join(ITEMS)
|
137 |
+
)
|
138 |
+
print("Prompt:\n" + prompt)
|
139 |
+
|
140 |
+
# Generate response
|
141 |
+
result = advisor.pipe(
|
142 |
+
prompt,
|
143 |
+
max_length=advisor.max_length,
|
144 |
+
early_stopping=True,
|
145 |
+
num_beams=4,
|
146 |
+
no_repeat_ngram_size=3,
|
147 |
+
do_sample=False)[0]["generated_text"].strip()
|
148 |
+
|
149 |
+
print("\nRaw generated output:\n" + result)
|
150 |
+
|
151 |
+
# Parse into tips (bullets or sentence)
|
152 |
+
lines = result.splitlines()
|
153 |
+
tips = [ln.strip("-*0123456789. ").strip() for ln in lines if ln.strip()]
|
154 |
+
if len(tips) < 3:
|
155 |
+
# fallback to sentence split
|
156 |
+
tips = [p.strip() for p in result.split(".") if p.strip()]
|
157 |
+
tips = list(dict.fromkeys(tips)) # remove duplicates
|
158 |
+
|
159 |
+
print(f"\n💡 Parsed {len(tips)} style tips:")
|
160 |
+
for i, tip in enumerate(tips[:5], 1):
|
161 |
+
print(f"{i}. {tip}")
|
162 |
+
print("-" * 40)
|
models/vision.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# models/vision.py -- Working
|
3 |
+
|
4 |
+
from transformers import pipeline
|
5 |
+
from PIL import Image
|
6 |
+
|
7 |
+
class VisionModel:
|
8 |
+
def __init__(
|
9 |
+
self,
|
10 |
+
model_name: str = "valentinafeve/yolos-fashionpedia",
|
11 |
+
threshold: float = 0.7
|
12 |
+
):
|
13 |
+
self.pipe = pipeline("object-detection", model=model_name)
|
14 |
+
self.threshold = threshold
|
15 |
+
|
16 |
+
def detect(self, image: Image.Image):
|
17 |
+
# 1) Ensure RGB
|
18 |
+
if image.mode != "RGB":
|
19 |
+
image = image.convert("RGB")
|
20 |
+
|
21 |
+
# 2) Run detection
|
22 |
+
results = self.pipe(image)
|
23 |
+
|
24 |
+
# 3) Process & filter
|
25 |
+
processed = []
|
26 |
+
for r in results:
|
27 |
+
score = float(r["score"])
|
28 |
+
if score < self.threshold:
|
29 |
+
continue
|
30 |
+
|
31 |
+
# r["box"] is a dict: {"xmin":..., "ymin":..., "xmax":..., "ymax":...}
|
32 |
+
box = r["box"]
|
33 |
+
coords = [
|
34 |
+
float(box["xmin"]),
|
35 |
+
float(box["ymin"]),
|
36 |
+
float(box["xmax"]),
|
37 |
+
float(box["ymax"]),
|
38 |
+
]
|
39 |
+
|
40 |
+
processed.append({
|
41 |
+
"label": r["label"],
|
42 |
+
"score": score,
|
43 |
+
"box": coords
|
44 |
+
})
|
45 |
+
|
46 |
+
return processed
|
47 |
+
|
48 |
+
|
49 |
+
|
50 |
+
|