@umituz/react-native-ai-generation-content 1.89.26 → 1.89.27

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.89.26",
3
+ "version": "1.89.27",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -20,7 +20,7 @@ export const GalleryScreenHeader: React.FC<GalleryScreenHeaderProps> = ({ title,
20
20
  const styles = useMemo(() => createStyles(tokens), [tokens]);
21
21
 
22
22
  return (
23
- <View style={[styles.screenHeader, { paddingTop: insets.top }]}>
23
+ <View style={styles.screenHeader}>
24
24
  <TouchableOpacity
25
25
  onPress={onBack}
26
26
  style={styles.backButton}
@@ -76,12 +76,7 @@ export function CreationsGalleryScreen({
76
76
 
77
77
  useAppFocusEffect(useCallback(() => {
78
78
  void refetch();
79
- // Reset selection on focus if no initial ID is being enforced
80
- if (!initialCreationId) {
81
- const { setSelectedCreation } = galleryState;
82
- setSelectedCreation(null);
83
- }
84
- }, [refetch, initialCreationId, galleryState.setSelectedCreation]));
79
+ }, [refetch]));
85
80
 
86
81
  const filterButtons = useMemo(() =>
87
82
  createFilterButtons({
@@ -165,7 +160,6 @@ export function CreationsGalleryScreen({
165
160
  {
166
161
  backgroundColor: tokens.colors.surface,
167
162
  borderBottomColor: tokens.colors.border,
168
- paddingTop: hasScreenHeader ? 0 : insets.top
169
163
  }
170
164
  ]}>
171
165
  <GalleryHeader
@@ -226,7 +220,7 @@ export function CreationsGalleryScreen({
226
220
  }
227
221
 
228
222
  return (
229
- <ScreenLayout header={screenHeader} scrollable={false} edges={["left", "right", "bottom"]}>
223
+ <ScreenLayout header={screenHeader} scrollable={false} edges={["top", "left", "right", "bottom"]}>
230
224
  {renderHeader}
231
225
  {filters.filtered.length === 0 ? (
232
226
  <View style={[styles.listContent, styles.emptyContent]}>
@@ -34,6 +34,20 @@ export type { GenerationResult } from "../../domain/entities/generation.types";
34
34
 
35
35
  export { ExecutorFactory, type GenerationType as ExecutorGenerationType } from "./infrastructure/executors/executor-factory";
36
36
 
37
+ // Couple Generation Utilities
38
+ export {
39
+ buildCoupleGenerationInput,
40
+ buildScenarioGenerationInput,
41
+ type CoupleGenerationInputParams,
42
+ type CoupleGenerationInput,
43
+ type ScenarioGenerationInputParams,
44
+ } from "./infrastructure/couple-generation-builder";
45
+
46
+ export {
47
+ getAppearanceContext,
48
+ enhancePromptWithAnalysis,
49
+ } from "./infrastructure/appearance-analysis";
50
+
37
51
  // Wizard Domain
38
52
  export type {
39
53
  BaseStepConfig,
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Appearance Analysis Utility
3
+ *
4
+ * Fotoğraf analizi ve appearance context çıkarma utility'si.
5
+ * Vision analysis暂时禁用 - 返回空上下文。
6
+ */
7
+
8
+ import { refinePromptForCouple } from "../../prompts";
9
+
10
+ /**
11
+ * Fotoğrafları analiz eder ve appearance context string'i döndürür.
12
+ * Şu an vision analysis devre dışı - boş string döndürür.
13
+ *
14
+ * @param photoUris - Analiz edilecek fotoğraf URI'leri
15
+ * @param isCoupleMode - Çift modu mu?
16
+ * @returns Appearance context string (şu an boş)
17
+ */
18
+ export async function getAppearanceContext(
19
+ photoUris: string[],
20
+ isCoupleMode: boolean,
21
+ ): Promise<string> {
22
+ // Vision analysis temporarily disabled due to API limitations
23
+ // Future: Implement vision analysis to extract appearance features
24
+ return "";
25
+ }
26
+
27
+ /**
28
+ * Fotoğraf analizi based prompt enhancement
29
+ *
30
+ * Tüm generation tab'larında kullanılan standard logic.
31
+ *
32
+ * @param originalPrompt - Orijinal prompt
33
+ * @param photoUris - Fotoğraf URI'leri
34
+ * @param isCoupleMode - Çift modu mu?
35
+ * @returns Enhanced prompt
36
+ */
37
+ export async function enhancePromptWithAnalysis(
38
+ originalPrompt: string,
39
+ photoUris: string[],
40
+ isCoupleMode: boolean,
41
+ ): Promise<string> {
42
+ // Always apply basic couple refinement first
43
+ let finalPrompt = refinePromptForCouple(originalPrompt, isCoupleMode);
44
+
45
+ if (photoUris.length === 0) return finalPrompt;
46
+
47
+ const appearanceContext = await getAppearanceContext(photoUris, isCoupleMode);
48
+
49
+ if (appearanceContext) {
50
+ finalPrompt = `${appearanceContext}\n\n${finalPrompt}`;
51
+ }
52
+
53
+ return finalPrompt;
54
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Couple Image Generation Builder
3
+ *
4
+ * Wardrobe'da kusursuz çalışan mantığı tüm couple generation için paylaştırır.
5
+ * Bu utility'yi tüm çift görüntü oluşturma işlemleri kullanır.
6
+ *
7
+ * Kullanım alanları:
8
+ * - Senaryo generation (Home couple images)
9
+ * - Wardrobe generation
10
+ * - Background generation
11
+ * - Art style generation
12
+ * - Mood filter generation
13
+ */
14
+
15
+ import {
16
+ resolveCoupleInput,
17
+ prependContext,
18
+ refinePromptForCouple,
19
+ createPhotorealisticPrompt,
20
+ } from "../../prompts";
21
+ import { getAppearanceContext } from "./appearance-analysis";
22
+
23
+ /**
24
+ * Generation target configuration
25
+ * TODO: Import from app config when available
26
+ */
27
+ const GENERATION_TARGETS = {
28
+ imageCoupleMultiRef: { model: "p-image-edit", providerId: "pruna" },
29
+ imageSingleEdit: { model: "p-image-edit", providerId: "pruna" },
30
+ } as const;
31
+
32
+ /**
33
+ * Couple generation input parameters
34
+ */
35
+ export interface CoupleGenerationInputParams {
36
+ // Required params
37
+ partner1PhotoUri: string;
38
+ partner2PhotoUri: string | null;
39
+ isCoupleMode: boolean;
40
+ basePrompt: string; // Scenario prompt, wardrobe prompt, background prompt, etc.
41
+
42
+ // Optional params
43
+ customInstructions?: string;
44
+ aspectRatio?: string; // Default: "3:4"
45
+ strength?: number; // Optional strength for some operations
46
+ }
47
+
48
+ /**
49
+ * Couple generation result
50
+ */
51
+ export interface CoupleGenerationInput {
52
+ target: string;
53
+ prompt: string;
54
+ params: Record<string, unknown>;
55
+ }
56
+
57
+ /**
58
+ * Scenario generation input parameters
59
+ */
60
+ export interface ScenarioGenerationInputParams {
61
+ partner1PhotoUri: string;
62
+ partner2PhotoUri: string | null;
63
+ isCoupleMode: boolean;
64
+ scenarioPrompt: string; // Senaryo prompt'u (aiPrompt)
65
+ customInstructions?: string;
66
+ }
67
+
68
+ /**
69
+ * Merkezi couple generation input builder
70
+ *
71
+ * Wardrobe mantığını tüm couple generation için paylaştırır:
72
+ * 1. Appearance analysis (fotoğrafları analiz et)
73
+ * 2. Couple refinement (çift modu için iyileştir)
74
+ * 3. Context prepending (context'i prompt'a ekle)
75
+ * 4. Photorealistic prompt creation
76
+ * 5. Couple input resolution (doğru target ve image'lar)
77
+ *
78
+ * @param params - Generation parameters
79
+ * @returns Generation input with target, prompt, and params
80
+ */
81
+ export async function buildCoupleGenerationInput(
82
+ params: CoupleGenerationInputParams,
83
+ ): Promise<CoupleGenerationInput> {
84
+ const {
85
+ partner1PhotoUri,
86
+ partner2PhotoUri,
87
+ isCoupleMode,
88
+ basePrompt,
89
+ customInstructions,
90
+ aspectRatio = "3:4", // Standard portrait ratio like Wardrobe
91
+ strength,
92
+ } = params;
93
+
94
+ // 1. GET PHOTO URIs - Couple mode kontrolü
95
+ const photoUris =
96
+ isCoupleMode && partner2PhotoUri
97
+ ? [partner1PhotoUri, partner2PhotoUri]
98
+ : [partner1PhotoUri];
99
+
100
+ // 2. ANALYZE APPEARANCE - Wardrobe'daki gibi
101
+ // Fotoğrafları analiz et ve context çıkar
102
+ const appearanceContext = await getAppearanceContext(
103
+ photoUris,
104
+ isCoupleMode,
105
+ );
106
+
107
+ // 3. REFINE FOR COUPLE + PREPEND CONTEXT - Wardrobe mantığı
108
+ // Coupler modu için prompt'u iyileştir ve context'i ekle
109
+ const refinedPrompt = prependContext(
110
+ refinePromptForCouple(basePrompt, isCoupleMode),
111
+ appearanceContext,
112
+ );
113
+
114
+ // 4. CREATE FINAL PROMPT - Photorealistic
115
+ const prompt = createPhotorealisticPrompt(refinedPrompt, {
116
+ isCouple: isCoupleMode,
117
+ customInstructions,
118
+ });
119
+
120
+ // 5. RESOLVE COUPLE INPUT - Doğru target ve image'lar
121
+ const { target, imageUrls } = resolveCoupleInput(
122
+ partner1PhotoUri,
123
+ partner2PhotoUri,
124
+ isCoupleMode,
125
+ GENERATION_TARGETS.imageSingleEdit,
126
+ GENERATION_TARGETS.imageCoupleMultiRef,
127
+ );
128
+
129
+ // 6. BUILD PARAMS - Wardrobe formatında
130
+ const genParams: Record<string, unknown> = {
131
+ prompt,
132
+ image_urls: imageUrls,
133
+ aspect_ratio: aspectRatio,
134
+ };
135
+
136
+ // Optional strength parameter
137
+ if (strength !== undefined) {
138
+ genParams.strength = strength;
139
+ }
140
+
141
+ return {
142
+ target,
143
+ prompt,
144
+ params: genParams,
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Scenario generation için özel wrapper
150
+ *
151
+ * Senaryo aiPrompt'larını (zaten createPhotorealisticPrompt ile oluşturulmuş)
152
+ * Wardrobe mantığı ile birleştirir.
153
+ */
154
+ export async function buildScenarioGenerationInput(
155
+ params: ScenarioGenerationInputParams,
156
+ ): Promise<CoupleGenerationInput> {
157
+ const {
158
+ partner1PhotoUri,
159
+ partner2PhotoUri,
160
+ isCoupleMode,
161
+ scenarioPrompt,
162
+ customInstructions,
163
+ } = params;
164
+
165
+ // 1. GET PHOTO URIs
166
+ const photoUris =
167
+ isCoupleMode && partner2PhotoUri
168
+ ? [partner1PhotoUri, partner2PhotoUri]
169
+ : [partner1PhotoUri];
170
+
171
+ // 2. ANALYZE APPEARANCE
172
+ const appearanceContext = await getAppearanceContext(
173
+ photoUris,
174
+ isCoupleMode,
175
+ );
176
+
177
+ // 3. REFINE FOR COUPLE + PREPEND CONTEXT
178
+ // Senaryo prompt'unu (zaten photorealistic) dynamic context ile birleştir
179
+ const finalPrompt = prependContext(
180
+ refinePromptForCouple(scenarioPrompt, isCoupleMode),
181
+ appearanceContext,
182
+ );
183
+
184
+ // 4. RESOLVE COUPLE INPUT
185
+ const { target, imageUrls } = resolveCoupleInput(
186
+ partner1PhotoUri,
187
+ partner2PhotoUri,
188
+ isCoupleMode,
189
+ GENERATION_TARGETS.imageSingleEdit,
190
+ GENERATION_TARGETS.imageCoupleMultiRef,
191
+ );
192
+
193
+ // 5. BUILD PARAMS - Scenario formatında
194
+ return {
195
+ target,
196
+ prompt: finalPrompt,
197
+ params: {
198
+ prompt: finalPrompt,
199
+ image_urls: imageUrls,
200
+ aspect_ratio: "3:4",
201
+ },
202
+ };
203
+ }
@@ -69,14 +69,16 @@ export async function executeImageGeneration(
69
69
 
70
70
  const modelInput: Record<string, unknown> = {
71
71
  prompt: finalPrompt,
72
- aspect_ratio: MODEL_INPUT_DEFAULTS.aspectRatio,
72
+ aspect_ratio: input.aspectRatio || MODEL_INPUT_DEFAULTS.aspectRatio,
73
73
  output_format: MODEL_INPUT_DEFAULTS.outputFormat,
74
74
  num_images: MODEL_INPUT_DEFAULTS.numImages,
75
75
  enable_safety_checker: MODEL_INPUT_DEFAULTS.enableSafetyChecker,
76
76
  };
77
77
 
78
- // nano-banana/edit uses image_urls (array) for both single and multi-image
78
+ // p-image-edit (and typical multi-ref models) usually expect 'images' key
79
+ // supporting both 'images' and 'image_urls' for maximum compatibility
79
80
  if (imageUrls.length > 0) {
81
+ modelInput.images = imageUrls;
80
82
  modelInput.image_urls = imageUrls;
81
83
  }
82
84
 
@@ -44,7 +44,10 @@ export async function buildImageInput(
44
44
  const styleValue = extractSelection(wizardData.style);
45
45
  const style = typeof styleValue === "string" ? styleValue : undefined;
46
46
 
47
- return { photos, prompt: finalPrompt, style };
47
+ // Extract aspect ratio from wizard data
48
+ const aspectRatio = typeof wizardData.aspect_ratio === "string" ? wizardData.aspect_ratio : undefined;
49
+
50
+ return { photos, prompt: finalPrompt, style, aspectRatio };
48
51
  }
49
52
 
50
53
  /**
@@ -11,6 +11,8 @@ export interface WizardImageInput {
11
11
  readonly prompt: string;
12
12
  /** Optional style from wizard selection (text-to-image only) */
13
13
  readonly style?: string;
14
+ /** Optional aspect ratio (passed to model) */
15
+ readonly aspectRatio?: string;
14
16
  }
15
17
 
16
18
  export interface CreateImageStrategyOptions {
@@ -13,11 +13,12 @@ Preserve the EXACT facial appearance, structure, and unique characteristics from
13
13
 
14
14
  export const IDENTITY_PRESERVATION_COUPLE = `IDENTITY PRESERVATION (CRITICAL):
15
15
  Preserve the EXACT facial appearance of BOTH people from the uploaded photos with 100% accuracy.
16
- - Maintain bone structure, facial proportions, and unique features for both individuals.
17
- - Keep natural skin texture and authentic expressions for both.
18
- - Both people must be instantly recognizable as themselves.
19
- - Ensure natural relative heights and chemistry between the two people.
20
- - DO NOT alter ethnicity, age, or apply artificial skin smoothing.`;
16
+ - MAPPING: Map the first person in the first photo to the first person in the scene, and the second person in the second photo to the second person in the scene.
17
+ - Maintain unique bone structure, facial proportions, and specific features for both individuals independently.
18
+ - Both people must be clearly distinct and instantly recognizable as their original selves.
19
+ - Keep authentic skin textures, expressions, and gender-specific traits for both.
20
+ - Ensure natural relative heights and genuine chemistry between the two.
21
+ - DO NOT mix facial features between the two people or apply artificial smoothing.`;
21
22
 
22
23
  export const PHOTOREALISTIC_RENDERING = `STYLE - PHOTOREALISTIC QUALITY (CRITICAL):
23
24
  Create an authentic photograph with professional camera characteristics.
@@ -47,3 +48,31 @@ Ensure a real photographic look, NOT digital art or AI-generated appearance.
47
48
  - Natural highlights and soft shadow falloff across faces.
48
49
  - Realistic fabric folds and individual hair movement.
49
50
  - NO poreless skin, artificial glow, or over-processed effects.`;
51
+
52
+ export const TECHNICAL_STANDARDS = `TECHNICAL QUALITY STANDARDS:
53
+ - Professional DSLR photograph quality (Canon/Nikon/Sony).
54
+ - Authentic 35mm film photography aesthetic, unfiltered raw capture.
55
+ - Fujifilm or Kodak Portra film color science - natural, organic, slightly muted.
56
+ - Natural uneven lighting with realistic deep shadows and highlights.
57
+ - Sharp focus on clothing but preserve raw skin texture with visible tiny pores.
58
+ - Result MUST NOT look AI-generated (Negative: plastic, render, CGI, 3d, airbrushed, doll, cartoon).`;
59
+
60
+ export const ARTISTIC_STANDARDS = `ARTISTIC QUALITY STANDARDS:
61
+ - High-fidelity digital art masterpiece.
62
+ - Deep texture details and masterful light/shadow interplay.
63
+ - Vibrant and harmonious color palette original to the selected theme.
64
+ - Sharp focus on subject identity while background blends into the artistic theme.
65
+ - NO blurred faces, NO distorted features.`;
66
+
67
+ export const WARDROBE_TRANSFORMATION_RULES = `WARDROBE TRANSFORMATION RULES:
68
+ - Change ONLY the clothing/outfits and apply accessories if requested.
69
+ - Keep ALL identity features IDENTICAL to reference photos.
70
+ - Core facial structure, hair color, and basic style must remain unchanged.`;
71
+
72
+ export const ART_STYLE_TRANSFORMATION_RULES = `ART STYLE TRANSFORMATION RULES:
73
+ - Apply the artistic style ONLY to the texture, lighting, and background.
74
+ - DO NOT change faces into generic art faces. They MUST be recognizable as the original individuals.`;
75
+
76
+ export const RETOUCH_TRANSFORMATION_RULES = `RETOUCH TRANSFORMATION RULES:
77
+ - Subtle, natural improvement while keeping original bone structure.
78
+ - NO extreme changes to identity. Keep facial markers (moles, freckles) if choice allows.`;
@@ -8,6 +8,11 @@ import {
8
8
  PHOTOREALISTIC_RENDERING,
9
9
  NATURAL_POSE_GUIDELINES,
10
10
  NATURAL_POSE_GUIDELINES_COUPLE,
11
+ TECHNICAL_STANDARDS,
12
+ ARTISTIC_STANDARDS,
13
+ WARDROBE_TRANSFORMATION_RULES,
14
+ ART_STYLE_TRANSFORMATION_RULES,
15
+ RETOUCH_TRANSFORMATION_RULES,
11
16
  } from "./constants";
12
17
  import type { CreatePromptOptions } from "./types";
13
18
 
@@ -20,28 +25,46 @@ export const createPhotorealisticPrompt = (
20
25
  includePhotoRealism = true,
21
26
  includePoseGuidelines = true,
22
27
  isCouple = false,
28
+ isArtistic = false,
29
+ isWardrobe = false,
30
+ isRetouch = false,
23
31
  customInstructions,
24
32
  } = options;
25
33
 
26
34
  const parts: string[] = [];
27
35
 
36
+ // 1. Identity Segment (Highest Priority)
28
37
  if (includeIdentityPreservation) {
29
38
  parts.push(isCouple ? IDENTITY_PRESERVATION_COUPLE : IDENTITY_PRESERVATION_CORE);
30
39
  }
31
40
 
41
+ // 2. Transformation Rules (Domain Specific)
42
+ if (isWardrobe) parts.push(WARDROBE_TRANSFORMATION_RULES);
43
+ if (isArtistic) parts.push(ART_STYLE_TRANSFORMATION_RULES);
44
+ if (isRetouch) parts.push(RETOUCH_TRANSFORMATION_RULES);
45
+
46
+ // 3. Technical Standards (Realism vs Artistic)
32
47
  if (includePhotoRealism) {
33
- parts.push(PHOTOREALISTIC_RENDERING);
48
+ parts.push(isArtistic ? ARTISTIC_STANDARDS : TECHNICAL_STANDARDS);
49
+ if (!isArtistic) parts.push(PHOTOREALISTIC_RENDERING);
34
50
  }
35
51
 
52
+ // 4. Pose Guidelines
36
53
  if (includePoseGuidelines) {
37
54
  parts.push(isCouple ? NATURAL_POSE_GUIDELINES_COUPLE : NATURAL_POSE_GUIDELINES);
38
55
  }
39
56
 
57
+ // 5. Custom Instructions (Appended here to allow overrides)
40
58
  if (customInstructions) {
41
59
  parts.push(customInstructions);
42
60
  }
43
61
 
44
- parts.push(`\nSCENARIO DESCRIPTION:\n${scenarioPrompt}`);
62
+ // 6. Final Scenario Context
63
+ if (isCouple && !scenarioPrompt.toLowerCase().includes("couple")) {
64
+ parts.push(`SCENARIO DESCRIPTION:\nA photo of a couple, ${scenarioPrompt}`);
65
+ } else {
66
+ parts.push(`SCENARIO DESCRIPTION:\n${scenarioPrompt}`);
67
+ }
45
68
 
46
69
  return parts.join('\n\n');
47
70
  };
@@ -7,5 +7,8 @@ export interface CreatePromptOptions {
7
7
  includePhotoRealism?: boolean;
8
8
  includePoseGuidelines?: boolean;
9
9
  isCouple?: boolean;
10
+ isArtistic?: boolean;
11
+ isWardrobe?: boolean;
12
+ isRetouch?: boolean;
10
13
  customInstructions?: string;
11
14
  }
@@ -9,6 +9,11 @@ export {
9
9
  PHOTOREALISTIC_RENDERING,
10
10
  NATURAL_POSE_GUIDELINES,
11
11
  NATURAL_POSE_GUIDELINES_COUPLE,
12
+ TECHNICAL_STANDARDS,
13
+ ARTISTIC_STANDARDS,
14
+ WARDROBE_TRANSFORMATION_RULES,
15
+ ART_STYLE_TRANSFORMATION_RULES,
16
+ RETOUCH_TRANSFORMATION_RULES,
12
17
  } from './domain/base/constants';
13
18
 
14
19
  export {
@@ -44,11 +44,20 @@ export const CREATIVE_BASE = {
44
44
  */
45
45
  export const createPhotorealisticPrompt = (
46
46
  scene: string,
47
- options?: { customInstructions?: string; isCouple?: boolean },
47
+ options?: {
48
+ customInstructions?: string;
49
+ isCouple?: boolean;
50
+ isArtistic?: boolean;
51
+ isWardrobe?: boolean;
52
+ isRetouch?: boolean;
53
+ },
48
54
  ): string => {
49
55
  return createUnifiedPrompt(scene, {
50
56
  customInstructions: options?.customInstructions,
51
57
  isCouple: options?.isCouple,
58
+ isArtistic: options?.isArtistic,
59
+ isWardrobe: options?.isWardrobe,
60
+ isRetouch: options?.isRetouch,
52
61
  });
53
62
  };
54
63
 
@@ -58,8 +58,21 @@ export function refinePromptForCouple(prompt: string, isCouple: boolean): string
58
58
  .replace(/\bidentical\b/gi, "identical for both individuals")
59
59
  .replace(/\binstantly recognizable\b/gi, "instantly recognizable as themselves");
60
60
 
61
+ // Special mapping for common couple scenarios: map "dress/gown" to person 1 and "suit/tuxedo" to person 2
62
+ // this helps the AI map reference images correctly to the scene roles
63
+ if (/\b(dress|gown)\b/i.test(refined) && /\b(suit|tuxedo|linen trousers)\b/i.test(refined)) {
64
+ refined = refined
65
+ .replace(/\b(dress|gown)\b/gi, "$1 (worn by Person 1)")
66
+ .replace(/\b(suit|tuxedo|linen trousers)\b/gi, "$1 (worn by Person 2)");
67
+
68
+ // Also add explicit mapping to the top if not already there
69
+ if (!refined.includes("DIRECTIVE: MAP PHOTO 1 TO PERSON 1")) {
70
+ refined = `DIRECTIVE: MAP PHOTO 1 TO PERSON 1, MAP PHOTO 2 TO PERSON 2\n\n${refined}`;
71
+ }
72
+ }
73
+
61
74
  // If it doesn't already have couple-hints, add them at the start
62
- if (!/\b(couple|both|matching|dual|two people)\b/i.test(refined)) {
75
+ if (!/\b(couple|both|matching|dual|two people)\b/i.test(refined) && !refined.includes("DIRECTIVE:")) {
63
76
  refined = `A photo of a couple, ${refined}`;
64
77
  }
65
78
  return refined;