vidspotai-shared 1.0.118 → 1.0.120
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/lib/libs/storage/MediaStorage.d.ts +4 -3
- package/lib/libs/storage/MediaStorage.d.ts.map +1 -1
- package/lib/services/agent/index.d.ts +1 -0
- package/lib/services/agent/index.d.ts.map +1 -1
- package/lib/services/agent/index.js +1 -0
- package/lib/services/agent/poster/index.d.ts +3 -0
- package/lib/services/agent/poster/index.d.ts.map +1 -0
- package/lib/services/agent/poster/index.js +18 -0
- package/lib/services/agent/poster/posterAgent.schema.d.ts +482 -0
- package/lib/services/agent/poster/posterAgent.schema.d.ts.map +1 -0
- package/lib/services/agent/poster/posterAgent.schema.js +296 -0
- package/lib/services/agent/poster/posterPlanner.d.ts +68 -0
- package/lib/services/agent/poster/posterPlanner.d.ts.map +1 -0
- package/lib/services/agent/poster/posterPlanner.js +343 -0
- package/package.json +6 -6
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* notes/R2_MEDIA_MIGRATION_PLAN.md §9 "Stage 2 decision log — R2-primary flip"):
|
|
13
13
|
* • BYTES = R2 — the only place object bytes are written, read, or
|
|
14
14
|
* deleted for anything going through `put`/`putFromFile`/`delete` below.
|
|
15
|
-
* • URL MINTER = GCS — used ONLY to mint the
|
|
15
|
+
* • URL MINTER = GCS — used ONLY to mint the V2-signed URL *shape* that
|
|
16
16
|
* the `cdn.vidspotai.com` Worker already recognizes, including its
|
|
17
|
-
* private-access gate (which just checks
|
|
17
|
+
* private-access gate (which just checks a signature param is
|
|
18
18
|
* present — see worker.js `privateRequestAllowed()`, it never
|
|
19
|
-
* cryptographically verifies it). V4
|
|
19
|
+
* cryptographically verifies it). V2 (not V4 — V4 caps expiry at 7
|
|
20
|
+
* days, and these need a far-future expiry) signing is a local crypto
|
|
20
21
|
* operation against the service-account key: no network call, and the
|
|
21
22
|
* object does NOT need to exist in the GCS bucket for a valid-shaped
|
|
22
23
|
* URL to be minted. So this eliminates real GCS *storage* (and its
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaStorage.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/MediaStorage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"MediaStorage.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/MediaStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,qBAAa,YAAY;IACvB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkB;gBAEhC,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe;IAK9D;;;;;;OAMG;IACG,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC;IAKlB;;;;;OAKG;IACG,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAMzC;;;;;;;OAOG;IACG,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;;OAKG;IACG,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;;;OAMG;IACG,kBAAkB,CACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAShB,iDAAiD;IACjD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlD;;;;;;;;OAQG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C;;;;;OAKG;YACW,gBAAgB;CAa/B;AAED,8EAA8E;AAC9E,eAAO,MAAM,YAAY,cAAsD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/agent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,UAAU,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/agent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,2BAA2B,CAAC;AAC1C,cAAc,UAAU,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
|
|
@@ -44,6 +44,7 @@ __exportStar(require("./extendChain"), exports);
|
|
|
44
44
|
__exportStar(require("./editExisting"), exports);
|
|
45
45
|
__exportStar(require("./repurpose"), exports);
|
|
46
46
|
__exportStar(require("./covers"), exports);
|
|
47
|
+
__exportStar(require("./poster"), exports);
|
|
47
48
|
__exportStar(require("./providerTaskCache"), exports);
|
|
48
49
|
__exportStar(require("./planDiff"), exports);
|
|
49
50
|
__exportStar(require("./taskPoller"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/poster/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./posterAgent.schema"), exports);
|
|
18
|
+
__exportStar(require("./posterPlanner"), exports);
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* AI Poster Studio — contracts for the prompt-first poster agent.
|
|
4
|
+
*
|
|
5
|
+
* One prompt ("summer sale, 40% off sneakers, ends Sunday") → the agent infers
|
|
6
|
+
* the use case, writes the copy, picks format/layout/palette/type, and writes
|
|
7
|
+
* the art prompt(s). Two render modes, chosen per run:
|
|
8
|
+
* - "editable": art is generated WITHOUT text (negative space left where the
|
|
9
|
+
* copy goes) and the copy is placed as real editable text layers on the
|
|
10
|
+
* canvas. Always spelled right; restyle in one click; animatable later
|
|
11
|
+
* (the video-poster / ad-studio path).
|
|
12
|
+
* - "flat": the copy is baked into the prompt and a typography-strong image
|
|
13
|
+
* model renders the whole poster as one image.
|
|
14
|
+
*
|
|
15
|
+
* Image-only for now; kept deliberately free of video concepts so a later
|
|
16
|
+
* "video poster / ad" mode can extend PosterVariantPlan rather than fork it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const POSTER_USE_CASE_IDS: readonly ["instagram-post", "instagram-story", "facebook-post", "linkedin-post", "x-post", "pinterest-pin", "youtube-thumbnail", "event-flyer", "sale-promo", "product-launch", "restaurant-food", "hiring", "webinar", "real-estate", "concert-gig", "fitness-class", "print-poster", "announcement"];
|
|
19
|
+
export declare const PosterUseCaseSchema: z.ZodEnum<{
|
|
20
|
+
"real-estate": "real-estate";
|
|
21
|
+
"instagram-post": "instagram-post";
|
|
22
|
+
"instagram-story": "instagram-story";
|
|
23
|
+
"facebook-post": "facebook-post";
|
|
24
|
+
"linkedin-post": "linkedin-post";
|
|
25
|
+
"x-post": "x-post";
|
|
26
|
+
"pinterest-pin": "pinterest-pin";
|
|
27
|
+
"youtube-thumbnail": "youtube-thumbnail";
|
|
28
|
+
"event-flyer": "event-flyer";
|
|
29
|
+
"sale-promo": "sale-promo";
|
|
30
|
+
"product-launch": "product-launch";
|
|
31
|
+
"restaurant-food": "restaurant-food";
|
|
32
|
+
hiring: "hiring";
|
|
33
|
+
webinar: "webinar";
|
|
34
|
+
"concert-gig": "concert-gig";
|
|
35
|
+
"fitness-class": "fitness-class";
|
|
36
|
+
"print-poster": "print-poster";
|
|
37
|
+
announcement: "announcement";
|
|
38
|
+
}>;
|
|
39
|
+
export type PosterUseCase = z.infer<typeof PosterUseCaseSchema>;
|
|
40
|
+
/** Aspect ratios every poster-agent image model accepts. */
|
|
41
|
+
export declare const POSTER_IMAGE_ASPECTS: readonly ["1:1", "3:4", "4:3", "9:16", "16:9"];
|
|
42
|
+
export type PosterImageAspect = (typeof POSTER_IMAGE_ASPECTS)[number];
|
|
43
|
+
export interface PosterFormat {
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
}
|
|
47
|
+
export interface PosterUseCaseSpec {
|
|
48
|
+
id: PosterUseCase;
|
|
49
|
+
label: string;
|
|
50
|
+
/** Canvas size the poster opens at (the user can still resize). */
|
|
51
|
+
format: PosterFormat;
|
|
52
|
+
/** Design guidance injected into the planner prompt for this use case. */
|
|
53
|
+
guidance: string;
|
|
54
|
+
}
|
|
55
|
+
export declare const POSTER_USE_CASES: Record<PosterUseCase, PosterUseCaseSpec>;
|
|
56
|
+
/**
|
|
57
|
+
* Curated families the editor canvas + server render preload
|
|
58
|
+
* (vidspotai-remotion-components CURATED_FONT_FAMILIES). The planner may ONLY
|
|
59
|
+
* pick from these so every poster renders with the intended face.
|
|
60
|
+
*/
|
|
61
|
+
export declare const POSTER_FONT_FAMILIES: readonly ["Inter", "Anton", "Bebas Neue", "Playfair Display", "Caveat", "Permanent Marker", "IBM Plex Sans", "Space Grotesk", "Archivo Black", "Outfit", "Fraunces", "DM Serif Display", "Poppins"];
|
|
62
|
+
export declare const PosterFontSchema: z.ZodEnum<{
|
|
63
|
+
Poppins: "Poppins";
|
|
64
|
+
Inter: "Inter";
|
|
65
|
+
Anton: "Anton";
|
|
66
|
+
"Bebas Neue": "Bebas Neue";
|
|
67
|
+
"Playfair Display": "Playfair Display";
|
|
68
|
+
Caveat: "Caveat";
|
|
69
|
+
"Permanent Marker": "Permanent Marker";
|
|
70
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
71
|
+
"Space Grotesk": "Space Grotesk";
|
|
72
|
+
"Archivo Black": "Archivo Black";
|
|
73
|
+
Outfit: "Outfit";
|
|
74
|
+
Fraunces: "Fraunces";
|
|
75
|
+
"DM Serif Display": "DM Serif Display";
|
|
76
|
+
}>;
|
|
77
|
+
export type PosterFont = z.infer<typeof PosterFontSchema>;
|
|
78
|
+
/**
|
|
79
|
+
* Layout archetypes. Each maps to fixed normalized text boxes on the client
|
|
80
|
+
* (poster-studio/agent/poster-layouts.ts), so layout is deterministic and
|
|
81
|
+
* always legible; the planner only chooses which one fits the art.
|
|
82
|
+
*
|
|
83
|
+
* hero-top big headline block in the top ~40%, art below
|
|
84
|
+
* hero-bottom art up top, copy stacked in the bottom ~40%
|
|
85
|
+
* center-stack everything centered over the art (needs a calm center)
|
|
86
|
+
* split-left copy in the left ~45% column, subject on the right
|
|
87
|
+
* split-right mirror of split-left
|
|
88
|
+
* framed copy on a solid panel/band across the lower third
|
|
89
|
+
* minimal-corner small refined copy block anchored bottom-left
|
|
90
|
+
*/
|
|
91
|
+
export declare const POSTER_LAYOUT_IDS: readonly ["hero-top", "hero-bottom", "center-stack", "split-left", "split-right", "framed", "minimal-corner"];
|
|
92
|
+
export declare const PosterLayoutSchema: z.ZodEnum<{
|
|
93
|
+
framed: "framed";
|
|
94
|
+
"hero-top": "hero-top";
|
|
95
|
+
"hero-bottom": "hero-bottom";
|
|
96
|
+
"center-stack": "center-stack";
|
|
97
|
+
"split-left": "split-left";
|
|
98
|
+
"split-right": "split-right";
|
|
99
|
+
"minimal-corner": "minimal-corner";
|
|
100
|
+
}>;
|
|
101
|
+
export type PosterLayout = z.infer<typeof PosterLayoutSchema>;
|
|
102
|
+
export declare const PosterRenderModeSchema: z.ZodEnum<{
|
|
103
|
+
flat: "flat";
|
|
104
|
+
editable: "editable";
|
|
105
|
+
}>;
|
|
106
|
+
export type PosterRenderMode = z.infer<typeof PosterRenderModeSchema>;
|
|
107
|
+
export declare const PosterPaletteSchema: z.ZodObject<{
|
|
108
|
+
background: z.ZodString;
|
|
109
|
+
primary: z.ZodString;
|
|
110
|
+
accent: z.ZodString;
|
|
111
|
+
text: z.ZodString;
|
|
112
|
+
onAccent: z.ZodString;
|
|
113
|
+
}, z.core.$strip>;
|
|
114
|
+
export type PosterPalette = z.infer<typeof PosterPaletteSchema>;
|
|
115
|
+
export declare const PosterCopySchema: z.ZodObject<{
|
|
116
|
+
headline: z.ZodString;
|
|
117
|
+
subheadline: z.ZodOptional<z.ZodString>;
|
|
118
|
+
body: z.ZodOptional<z.ZodString>;
|
|
119
|
+
cta: z.ZodOptional<z.ZodString>;
|
|
120
|
+
details: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
121
|
+
footer: z.ZodOptional<z.ZodString>;
|
|
122
|
+
}, z.core.$strip>;
|
|
123
|
+
export type PosterCopy = z.infer<typeof PosterCopySchema>;
|
|
124
|
+
export declare const PosterVariantPlanSchema: z.ZodObject<{
|
|
125
|
+
concept: z.ZodString;
|
|
126
|
+
layout: z.ZodEnum<{
|
|
127
|
+
framed: "framed";
|
|
128
|
+
"hero-top": "hero-top";
|
|
129
|
+
"hero-bottom": "hero-bottom";
|
|
130
|
+
"center-stack": "center-stack";
|
|
131
|
+
"split-left": "split-left";
|
|
132
|
+
"split-right": "split-right";
|
|
133
|
+
"minimal-corner": "minimal-corner";
|
|
134
|
+
}>;
|
|
135
|
+
palette: z.ZodObject<{
|
|
136
|
+
background: z.ZodString;
|
|
137
|
+
primary: z.ZodString;
|
|
138
|
+
accent: z.ZodString;
|
|
139
|
+
text: z.ZodString;
|
|
140
|
+
onAccent: z.ZodString;
|
|
141
|
+
}, z.core.$strip>;
|
|
142
|
+
typography: z.ZodObject<{
|
|
143
|
+
headingFont: z.ZodEnum<{
|
|
144
|
+
Poppins: "Poppins";
|
|
145
|
+
Inter: "Inter";
|
|
146
|
+
Anton: "Anton";
|
|
147
|
+
"Bebas Neue": "Bebas Neue";
|
|
148
|
+
"Playfair Display": "Playfair Display";
|
|
149
|
+
Caveat: "Caveat";
|
|
150
|
+
"Permanent Marker": "Permanent Marker";
|
|
151
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
152
|
+
"Space Grotesk": "Space Grotesk";
|
|
153
|
+
"Archivo Black": "Archivo Black";
|
|
154
|
+
Outfit: "Outfit";
|
|
155
|
+
Fraunces: "Fraunces";
|
|
156
|
+
"DM Serif Display": "DM Serif Display";
|
|
157
|
+
}>;
|
|
158
|
+
bodyFont: z.ZodEnum<{
|
|
159
|
+
Poppins: "Poppins";
|
|
160
|
+
Inter: "Inter";
|
|
161
|
+
Anton: "Anton";
|
|
162
|
+
"Bebas Neue": "Bebas Neue";
|
|
163
|
+
"Playfair Display": "Playfair Display";
|
|
164
|
+
Caveat: "Caveat";
|
|
165
|
+
"Permanent Marker": "Permanent Marker";
|
|
166
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
167
|
+
"Space Grotesk": "Space Grotesk";
|
|
168
|
+
"Archivo Black": "Archivo Black";
|
|
169
|
+
Outfit: "Outfit";
|
|
170
|
+
Fraunces: "Fraunces";
|
|
171
|
+
"DM Serif Display": "DM Serif Display";
|
|
172
|
+
}>;
|
|
173
|
+
headingCase: z.ZodEnum<{
|
|
174
|
+
title: "title";
|
|
175
|
+
upper: "upper";
|
|
176
|
+
sentence: "sentence";
|
|
177
|
+
}>;
|
|
178
|
+
}, z.core.$strip>;
|
|
179
|
+
copy: z.ZodObject<{
|
|
180
|
+
headline: z.ZodString;
|
|
181
|
+
subheadline: z.ZodOptional<z.ZodString>;
|
|
182
|
+
body: z.ZodOptional<z.ZodString>;
|
|
183
|
+
cta: z.ZodOptional<z.ZodString>;
|
|
184
|
+
details: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
185
|
+
footer: z.ZodOptional<z.ZodString>;
|
|
186
|
+
}, z.core.$strip>;
|
|
187
|
+
scrim: z.ZodEnum<{
|
|
188
|
+
none: "none";
|
|
189
|
+
soft: "soft";
|
|
190
|
+
strong: "strong";
|
|
191
|
+
}>;
|
|
192
|
+
imagePrompt: z.ZodString;
|
|
193
|
+
negativePrompt: z.ZodOptional<z.ZodString>;
|
|
194
|
+
}, z.core.$strip>;
|
|
195
|
+
export type PosterVariantPlan = z.infer<typeof PosterVariantPlanSchema>;
|
|
196
|
+
export declare const PosterPlanSchema: z.ZodObject<{
|
|
197
|
+
useCase: z.ZodEnum<{
|
|
198
|
+
"real-estate": "real-estate";
|
|
199
|
+
"instagram-post": "instagram-post";
|
|
200
|
+
"instagram-story": "instagram-story";
|
|
201
|
+
"facebook-post": "facebook-post";
|
|
202
|
+
"linkedin-post": "linkedin-post";
|
|
203
|
+
"x-post": "x-post";
|
|
204
|
+
"pinterest-pin": "pinterest-pin";
|
|
205
|
+
"youtube-thumbnail": "youtube-thumbnail";
|
|
206
|
+
"event-flyer": "event-flyer";
|
|
207
|
+
"sale-promo": "sale-promo";
|
|
208
|
+
"product-launch": "product-launch";
|
|
209
|
+
"restaurant-food": "restaurant-food";
|
|
210
|
+
hiring: "hiring";
|
|
211
|
+
webinar: "webinar";
|
|
212
|
+
"concert-gig": "concert-gig";
|
|
213
|
+
"fitness-class": "fitness-class";
|
|
214
|
+
"print-poster": "print-poster";
|
|
215
|
+
announcement: "announcement";
|
|
216
|
+
}>;
|
|
217
|
+
mode: z.ZodEnum<{
|
|
218
|
+
flat: "flat";
|
|
219
|
+
editable: "editable";
|
|
220
|
+
}>;
|
|
221
|
+
format: z.ZodObject<{
|
|
222
|
+
width: z.ZodNumber;
|
|
223
|
+
height: z.ZodNumber;
|
|
224
|
+
}, z.core.$strip>;
|
|
225
|
+
imageAspect: z.ZodEnum<{
|
|
226
|
+
"9:16": "9:16";
|
|
227
|
+
"1:1": "1:1";
|
|
228
|
+
"16:9": "16:9";
|
|
229
|
+
"4:3": "4:3";
|
|
230
|
+
"3:4": "3:4";
|
|
231
|
+
}>;
|
|
232
|
+
audience: z.ZodString;
|
|
233
|
+
goal: z.ZodString;
|
|
234
|
+
tone: z.ZodString;
|
|
235
|
+
variants: z.ZodArray<z.ZodObject<{
|
|
236
|
+
concept: z.ZodString;
|
|
237
|
+
layout: z.ZodEnum<{
|
|
238
|
+
framed: "framed";
|
|
239
|
+
"hero-top": "hero-top";
|
|
240
|
+
"hero-bottom": "hero-bottom";
|
|
241
|
+
"center-stack": "center-stack";
|
|
242
|
+
"split-left": "split-left";
|
|
243
|
+
"split-right": "split-right";
|
|
244
|
+
"minimal-corner": "minimal-corner";
|
|
245
|
+
}>;
|
|
246
|
+
palette: z.ZodObject<{
|
|
247
|
+
background: z.ZodString;
|
|
248
|
+
primary: z.ZodString;
|
|
249
|
+
accent: z.ZodString;
|
|
250
|
+
text: z.ZodString;
|
|
251
|
+
onAccent: z.ZodString;
|
|
252
|
+
}, z.core.$strip>;
|
|
253
|
+
typography: z.ZodObject<{
|
|
254
|
+
headingFont: z.ZodEnum<{
|
|
255
|
+
Poppins: "Poppins";
|
|
256
|
+
Inter: "Inter";
|
|
257
|
+
Anton: "Anton";
|
|
258
|
+
"Bebas Neue": "Bebas Neue";
|
|
259
|
+
"Playfair Display": "Playfair Display";
|
|
260
|
+
Caveat: "Caveat";
|
|
261
|
+
"Permanent Marker": "Permanent Marker";
|
|
262
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
263
|
+
"Space Grotesk": "Space Grotesk";
|
|
264
|
+
"Archivo Black": "Archivo Black";
|
|
265
|
+
Outfit: "Outfit";
|
|
266
|
+
Fraunces: "Fraunces";
|
|
267
|
+
"DM Serif Display": "DM Serif Display";
|
|
268
|
+
}>;
|
|
269
|
+
bodyFont: z.ZodEnum<{
|
|
270
|
+
Poppins: "Poppins";
|
|
271
|
+
Inter: "Inter";
|
|
272
|
+
Anton: "Anton";
|
|
273
|
+
"Bebas Neue": "Bebas Neue";
|
|
274
|
+
"Playfair Display": "Playfair Display";
|
|
275
|
+
Caveat: "Caveat";
|
|
276
|
+
"Permanent Marker": "Permanent Marker";
|
|
277
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
278
|
+
"Space Grotesk": "Space Grotesk";
|
|
279
|
+
"Archivo Black": "Archivo Black";
|
|
280
|
+
Outfit: "Outfit";
|
|
281
|
+
Fraunces: "Fraunces";
|
|
282
|
+
"DM Serif Display": "DM Serif Display";
|
|
283
|
+
}>;
|
|
284
|
+
headingCase: z.ZodEnum<{
|
|
285
|
+
title: "title";
|
|
286
|
+
upper: "upper";
|
|
287
|
+
sentence: "sentence";
|
|
288
|
+
}>;
|
|
289
|
+
}, z.core.$strip>;
|
|
290
|
+
copy: z.ZodObject<{
|
|
291
|
+
headline: z.ZodString;
|
|
292
|
+
subheadline: z.ZodOptional<z.ZodString>;
|
|
293
|
+
body: z.ZodOptional<z.ZodString>;
|
|
294
|
+
cta: z.ZodOptional<z.ZodString>;
|
|
295
|
+
details: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
296
|
+
footer: z.ZodOptional<z.ZodString>;
|
|
297
|
+
}, z.core.$strip>;
|
|
298
|
+
scrim: z.ZodEnum<{
|
|
299
|
+
none: "none";
|
|
300
|
+
soft: "soft";
|
|
301
|
+
strong: "strong";
|
|
302
|
+
}>;
|
|
303
|
+
imagePrompt: z.ZodString;
|
|
304
|
+
negativePrompt: z.ZodOptional<z.ZodString>;
|
|
305
|
+
}, z.core.$strip>>;
|
|
306
|
+
}, z.core.$strip>;
|
|
307
|
+
export type PosterPlan = z.infer<typeof PosterPlanSchema>;
|
|
308
|
+
export declare const PosterAgentRequestSchema: z.ZodObject<{
|
|
309
|
+
prompt: z.ZodString;
|
|
310
|
+
useCase: z.ZodOptional<z.ZodEnum<{
|
|
311
|
+
"real-estate": "real-estate";
|
|
312
|
+
"instagram-post": "instagram-post";
|
|
313
|
+
"instagram-story": "instagram-story";
|
|
314
|
+
"facebook-post": "facebook-post";
|
|
315
|
+
"linkedin-post": "linkedin-post";
|
|
316
|
+
"x-post": "x-post";
|
|
317
|
+
"pinterest-pin": "pinterest-pin";
|
|
318
|
+
"youtube-thumbnail": "youtube-thumbnail";
|
|
319
|
+
"event-flyer": "event-flyer";
|
|
320
|
+
"sale-promo": "sale-promo";
|
|
321
|
+
"product-launch": "product-launch";
|
|
322
|
+
"restaurant-food": "restaurant-food";
|
|
323
|
+
hiring: "hiring";
|
|
324
|
+
webinar: "webinar";
|
|
325
|
+
"concert-gig": "concert-gig";
|
|
326
|
+
"fitness-class": "fitness-class";
|
|
327
|
+
"print-poster": "print-poster";
|
|
328
|
+
announcement: "announcement";
|
|
329
|
+
}>>;
|
|
330
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
331
|
+
flat: "flat";
|
|
332
|
+
editable: "editable";
|
|
333
|
+
}>>;
|
|
334
|
+
variants: z.ZodDefault<z.ZodNumber>;
|
|
335
|
+
format: z.ZodOptional<z.ZodObject<{
|
|
336
|
+
width: z.ZodNumber;
|
|
337
|
+
height: z.ZodNumber;
|
|
338
|
+
}, z.core.$strip>>;
|
|
339
|
+
referenceImageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
340
|
+
useBrandKit: z.ZodDefault<z.ZodBoolean>;
|
|
341
|
+
language: z.ZodOptional<z.ZodString>;
|
|
342
|
+
}, z.core.$strip>;
|
|
343
|
+
export type PosterAgentRequest = z.infer<typeof PosterAgentRequestSchema>;
|
|
344
|
+
export declare const PosterRefineRequestSchema: z.ZodObject<{
|
|
345
|
+
plan: z.ZodObject<{
|
|
346
|
+
useCase: z.ZodEnum<{
|
|
347
|
+
"real-estate": "real-estate";
|
|
348
|
+
"instagram-post": "instagram-post";
|
|
349
|
+
"instagram-story": "instagram-story";
|
|
350
|
+
"facebook-post": "facebook-post";
|
|
351
|
+
"linkedin-post": "linkedin-post";
|
|
352
|
+
"x-post": "x-post";
|
|
353
|
+
"pinterest-pin": "pinterest-pin";
|
|
354
|
+
"youtube-thumbnail": "youtube-thumbnail";
|
|
355
|
+
"event-flyer": "event-flyer";
|
|
356
|
+
"sale-promo": "sale-promo";
|
|
357
|
+
"product-launch": "product-launch";
|
|
358
|
+
"restaurant-food": "restaurant-food";
|
|
359
|
+
hiring: "hiring";
|
|
360
|
+
webinar: "webinar";
|
|
361
|
+
"concert-gig": "concert-gig";
|
|
362
|
+
"fitness-class": "fitness-class";
|
|
363
|
+
"print-poster": "print-poster";
|
|
364
|
+
announcement: "announcement";
|
|
365
|
+
}>;
|
|
366
|
+
mode: z.ZodEnum<{
|
|
367
|
+
flat: "flat";
|
|
368
|
+
editable: "editable";
|
|
369
|
+
}>;
|
|
370
|
+
format: z.ZodObject<{
|
|
371
|
+
width: z.ZodNumber;
|
|
372
|
+
height: z.ZodNumber;
|
|
373
|
+
}, z.core.$strip>;
|
|
374
|
+
imageAspect: z.ZodEnum<{
|
|
375
|
+
"9:16": "9:16";
|
|
376
|
+
"1:1": "1:1";
|
|
377
|
+
"16:9": "16:9";
|
|
378
|
+
"4:3": "4:3";
|
|
379
|
+
"3:4": "3:4";
|
|
380
|
+
}>;
|
|
381
|
+
audience: z.ZodString;
|
|
382
|
+
goal: z.ZodString;
|
|
383
|
+
tone: z.ZodString;
|
|
384
|
+
variants: z.ZodArray<z.ZodObject<{
|
|
385
|
+
concept: z.ZodString;
|
|
386
|
+
layout: z.ZodEnum<{
|
|
387
|
+
framed: "framed";
|
|
388
|
+
"hero-top": "hero-top";
|
|
389
|
+
"hero-bottom": "hero-bottom";
|
|
390
|
+
"center-stack": "center-stack";
|
|
391
|
+
"split-left": "split-left";
|
|
392
|
+
"split-right": "split-right";
|
|
393
|
+
"minimal-corner": "minimal-corner";
|
|
394
|
+
}>;
|
|
395
|
+
palette: z.ZodObject<{
|
|
396
|
+
background: z.ZodString;
|
|
397
|
+
primary: z.ZodString;
|
|
398
|
+
accent: z.ZodString;
|
|
399
|
+
text: z.ZodString;
|
|
400
|
+
onAccent: z.ZodString;
|
|
401
|
+
}, z.core.$strip>;
|
|
402
|
+
typography: z.ZodObject<{
|
|
403
|
+
headingFont: z.ZodEnum<{
|
|
404
|
+
Poppins: "Poppins";
|
|
405
|
+
Inter: "Inter";
|
|
406
|
+
Anton: "Anton";
|
|
407
|
+
"Bebas Neue": "Bebas Neue";
|
|
408
|
+
"Playfair Display": "Playfair Display";
|
|
409
|
+
Caveat: "Caveat";
|
|
410
|
+
"Permanent Marker": "Permanent Marker";
|
|
411
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
412
|
+
"Space Grotesk": "Space Grotesk";
|
|
413
|
+
"Archivo Black": "Archivo Black";
|
|
414
|
+
Outfit: "Outfit";
|
|
415
|
+
Fraunces: "Fraunces";
|
|
416
|
+
"DM Serif Display": "DM Serif Display";
|
|
417
|
+
}>;
|
|
418
|
+
bodyFont: z.ZodEnum<{
|
|
419
|
+
Poppins: "Poppins";
|
|
420
|
+
Inter: "Inter";
|
|
421
|
+
Anton: "Anton";
|
|
422
|
+
"Bebas Neue": "Bebas Neue";
|
|
423
|
+
"Playfair Display": "Playfair Display";
|
|
424
|
+
Caveat: "Caveat";
|
|
425
|
+
"Permanent Marker": "Permanent Marker";
|
|
426
|
+
"IBM Plex Sans": "IBM Plex Sans";
|
|
427
|
+
"Space Grotesk": "Space Grotesk";
|
|
428
|
+
"Archivo Black": "Archivo Black";
|
|
429
|
+
Outfit: "Outfit";
|
|
430
|
+
Fraunces: "Fraunces";
|
|
431
|
+
"DM Serif Display": "DM Serif Display";
|
|
432
|
+
}>;
|
|
433
|
+
headingCase: z.ZodEnum<{
|
|
434
|
+
title: "title";
|
|
435
|
+
upper: "upper";
|
|
436
|
+
sentence: "sentence";
|
|
437
|
+
}>;
|
|
438
|
+
}, z.core.$strip>;
|
|
439
|
+
copy: z.ZodObject<{
|
|
440
|
+
headline: z.ZodString;
|
|
441
|
+
subheadline: z.ZodOptional<z.ZodString>;
|
|
442
|
+
body: z.ZodOptional<z.ZodString>;
|
|
443
|
+
cta: z.ZodOptional<z.ZodString>;
|
|
444
|
+
details: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
445
|
+
footer: z.ZodOptional<z.ZodString>;
|
|
446
|
+
}, z.core.$strip>;
|
|
447
|
+
scrim: z.ZodEnum<{
|
|
448
|
+
none: "none";
|
|
449
|
+
soft: "soft";
|
|
450
|
+
strong: "strong";
|
|
451
|
+
}>;
|
|
452
|
+
imagePrompt: z.ZodString;
|
|
453
|
+
negativePrompt: z.ZodOptional<z.ZodString>;
|
|
454
|
+
}, z.core.$strip>>;
|
|
455
|
+
}, z.core.$strip>;
|
|
456
|
+
variantIndex: z.ZodNumber;
|
|
457
|
+
instruction: z.ZodString;
|
|
458
|
+
referenceImageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
459
|
+
useBrandKit: z.ZodDefault<z.ZodBoolean>;
|
|
460
|
+
}, z.core.$strip>;
|
|
461
|
+
export type PosterRefineRequest = z.infer<typeof PosterRefineRequestSchema>;
|
|
462
|
+
/** Brand context the controller resolves from the user's brand kit. */
|
|
463
|
+
export interface PosterBrandContext {
|
|
464
|
+
brandName?: string;
|
|
465
|
+
colors?: {
|
|
466
|
+
primary?: string;
|
|
467
|
+
secondary?: string;
|
|
468
|
+
accent?: string;
|
|
469
|
+
palette?: string[];
|
|
470
|
+
};
|
|
471
|
+
fonts?: {
|
|
472
|
+
heading?: string;
|
|
473
|
+
body?: string;
|
|
474
|
+
};
|
|
475
|
+
tone?: string;
|
|
476
|
+
hasLogo?: boolean;
|
|
477
|
+
/** Brand-kit banned claims — never appear in the copy. */
|
|
478
|
+
avoidPhrases?: string[];
|
|
479
|
+
}
|
|
480
|
+
/** Pick the image aspect closest to a canvas format. */
|
|
481
|
+
export declare function nearestPosterAspect(format: PosterFormat): PosterImageAspect;
|
|
482
|
+
//# sourceMappingURL=posterAgent.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posterAgent.schema.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/poster/posterAgent.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;GAeG;AAIH,eAAO,MAAM,mBAAmB,wSAmBtB,CAAC;AACX,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;EAA8B,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,gDAAiD,CAAC;AACnF,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,aAAa,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,EAAE,YAAY,CAAC;IACrB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,aAAa,EAAE,iBAAiB,CA2HrE,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,qMAcvB,CAAC;AACX,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;EAA+B,CAAC;AAC7D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,+GAQpB,CAAC;AACX,eAAO,MAAM,kBAAkB;;;;;;;;EAA4B,CAAC;AAC5D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,sBAAsB;;;EAA+B,CAAC;AACnE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,eAAO,MAAM,mBAAmB;;;;;;iBAW9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,gBAAgB;;;;;;;iBAQ3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmBlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAU3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI1D,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAMpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACvF,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,iBAAiB,CAa3E"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PosterRefineRequestSchema = exports.PosterAgentRequestSchema = exports.PosterPlanSchema = exports.PosterVariantPlanSchema = exports.PosterCopySchema = exports.PosterPaletteSchema = exports.PosterRenderModeSchema = exports.PosterLayoutSchema = exports.POSTER_LAYOUT_IDS = exports.PosterFontSchema = exports.POSTER_FONT_FAMILIES = exports.POSTER_USE_CASES = exports.POSTER_IMAGE_ASPECTS = exports.PosterUseCaseSchema = exports.POSTER_USE_CASE_IDS = void 0;
|
|
4
|
+
exports.nearestPosterAspect = nearestPosterAspect;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
/**
|
|
7
|
+
* AI Poster Studio — contracts for the prompt-first poster agent.
|
|
8
|
+
*
|
|
9
|
+
* One prompt ("summer sale, 40% off sneakers, ends Sunday") → the agent infers
|
|
10
|
+
* the use case, writes the copy, picks format/layout/palette/type, and writes
|
|
11
|
+
* the art prompt(s). Two render modes, chosen per run:
|
|
12
|
+
* - "editable": art is generated WITHOUT text (negative space left where the
|
|
13
|
+
* copy goes) and the copy is placed as real editable text layers on the
|
|
14
|
+
* canvas. Always spelled right; restyle in one click; animatable later
|
|
15
|
+
* (the video-poster / ad-studio path).
|
|
16
|
+
* - "flat": the copy is baked into the prompt and a typography-strong image
|
|
17
|
+
* model renders the whole poster as one image.
|
|
18
|
+
*
|
|
19
|
+
* Image-only for now; kept deliberately free of video concepts so a later
|
|
20
|
+
* "video poster / ad" mode can extend PosterVariantPlan rather than fork it.
|
|
21
|
+
*/
|
|
22
|
+
// ── Use cases ──────────────────────────────────────────────────────────────
|
|
23
|
+
exports.POSTER_USE_CASE_IDS = [
|
|
24
|
+
"instagram-post",
|
|
25
|
+
"instagram-story",
|
|
26
|
+
"facebook-post",
|
|
27
|
+
"linkedin-post",
|
|
28
|
+
"x-post",
|
|
29
|
+
"pinterest-pin",
|
|
30
|
+
"youtube-thumbnail",
|
|
31
|
+
"event-flyer",
|
|
32
|
+
"sale-promo",
|
|
33
|
+
"product-launch",
|
|
34
|
+
"restaurant-food",
|
|
35
|
+
"hiring",
|
|
36
|
+
"webinar",
|
|
37
|
+
"real-estate",
|
|
38
|
+
"concert-gig",
|
|
39
|
+
"fitness-class",
|
|
40
|
+
"print-poster",
|
|
41
|
+
"announcement",
|
|
42
|
+
];
|
|
43
|
+
exports.PosterUseCaseSchema = zod_1.z.enum(exports.POSTER_USE_CASE_IDS);
|
|
44
|
+
/** Aspect ratios every poster-agent image model accepts. */
|
|
45
|
+
exports.POSTER_IMAGE_ASPECTS = ["1:1", "3:4", "4:3", "9:16", "16:9"];
|
|
46
|
+
exports.POSTER_USE_CASES = {
|
|
47
|
+
"instagram-post": {
|
|
48
|
+
id: "instagram-post",
|
|
49
|
+
label: "Instagram post",
|
|
50
|
+
format: { width: 1080, height: 1350 },
|
|
51
|
+
guidance: "Feed-scroll stopper. One bold idea, headline readable at thumbnail size, max ~8 headline words. Keep critical copy away from the bottom 10% (UI overlays).",
|
|
52
|
+
},
|
|
53
|
+
"instagram-story": {
|
|
54
|
+
id: "instagram-story",
|
|
55
|
+
label: "Instagram / TikTok story",
|
|
56
|
+
format: { width: 1080, height: 1920 },
|
|
57
|
+
guidance: "Full-screen vertical. Keep the top 250px and bottom 340px free of text (profile bar + reply bar). Big central headline, a clear CTA like 'Swipe up' / 'Link in bio'.",
|
|
58
|
+
},
|
|
59
|
+
"facebook-post": {
|
|
60
|
+
id: "facebook-post",
|
|
61
|
+
label: "Facebook post",
|
|
62
|
+
format: { width: 1200, height: 1500 },
|
|
63
|
+
guidance: "Friendly and direct, slightly more copy tolerated than Instagram. Clear CTA.",
|
|
64
|
+
},
|
|
65
|
+
"linkedin-post": {
|
|
66
|
+
id: "linkedin-post",
|
|
67
|
+
label: "LinkedIn post",
|
|
68
|
+
format: { width: 1200, height: 1200 },
|
|
69
|
+
guidance: "Professional, credible, uncluttered. Prefer clean sans type, generous whitespace, data or benefit-led headline. Avoid hype and emoji.",
|
|
70
|
+
},
|
|
71
|
+
"x-post": {
|
|
72
|
+
id: "x-post",
|
|
73
|
+
label: "X / Twitter post",
|
|
74
|
+
format: { width: 1600, height: 900 },
|
|
75
|
+
guidance: "Landscape card; headline must survive being cropped to the center 16:9.",
|
|
76
|
+
},
|
|
77
|
+
"pinterest-pin": {
|
|
78
|
+
id: "pinterest-pin",
|
|
79
|
+
label: "Pinterest pin",
|
|
80
|
+
format: { width: 1000, height: 1500 },
|
|
81
|
+
guidance: "Tall and aspirational. Headline in the top third, image-led, soft editorial look.",
|
|
82
|
+
},
|
|
83
|
+
"youtube-thumbnail": {
|
|
84
|
+
id: "youtube-thumbnail",
|
|
85
|
+
label: "YouTube thumbnail",
|
|
86
|
+
format: { width: 1280, height: 720 },
|
|
87
|
+
guidance: "3 words or fewer in huge heavy type, extreme contrast, one expressive subject. Nothing important in the bottom-right corner (timestamp badge).",
|
|
88
|
+
},
|
|
89
|
+
"event-flyer": {
|
|
90
|
+
id: "event-flyer",
|
|
91
|
+
label: "Event flyer",
|
|
92
|
+
format: { width: 1240, height: 1754 },
|
|
93
|
+
guidance: "Hierarchy: event name → date/time → venue → CTA (tickets/RSVP). Details as short scannable lines. Put date, time, and place in `details`.",
|
|
94
|
+
},
|
|
95
|
+
"sale-promo": {
|
|
96
|
+
id: "sale-promo",
|
|
97
|
+
label: "Sale / promotion",
|
|
98
|
+
format: { width: 1080, height: 1350 },
|
|
99
|
+
guidance: "The offer IS the headline (e.g. '40% OFF'). Urgency line (ends Sunday), a promo code or 'Shop now' CTA. High-energy palette, heavy display type.",
|
|
100
|
+
},
|
|
101
|
+
"product-launch": {
|
|
102
|
+
id: "product-launch",
|
|
103
|
+
label: "Product launch",
|
|
104
|
+
format: { width: 1080, height: 1350 },
|
|
105
|
+
guidance: "Hero the product with studio lighting and room around it. Headline = the product's single biggest benefit; subheadline names the product; CTA 'Available now' / 'Pre-order'.",
|
|
106
|
+
},
|
|
107
|
+
"restaurant-food": {
|
|
108
|
+
id: "restaurant-food",
|
|
109
|
+
label: "Restaurant / food special",
|
|
110
|
+
format: { width: 1080, height: 1350 },
|
|
111
|
+
guidance: "Appetite appeal first: close, warm, glossy food photography. Dish name as headline, price in `details`, hours/location in footer.",
|
|
112
|
+
},
|
|
113
|
+
hiring: {
|
|
114
|
+
id: "hiring",
|
|
115
|
+
label: "We're hiring",
|
|
116
|
+
format: { width: 1200, height: 1200 },
|
|
117
|
+
guidance: "Headline 'We're hiring' or the role name. List 2–4 roles or perks as `details`. CTA 'Apply now' + where. Human, welcoming tone.",
|
|
118
|
+
},
|
|
119
|
+
webinar: {
|
|
120
|
+
id: "webinar",
|
|
121
|
+
label: "Webinar / talk",
|
|
122
|
+
format: { width: 1200, height: 1200 },
|
|
123
|
+
guidance: "Topic as headline, speaker name/title as subheadline, date + time + timezone in `details`, CTA 'Register free'. Clean, trustworthy.",
|
|
124
|
+
},
|
|
125
|
+
"real-estate": {
|
|
126
|
+
id: "real-estate",
|
|
127
|
+
label: "Real estate listing",
|
|
128
|
+
format: { width: 1080, height: 1350 },
|
|
129
|
+
guidance: "Bright, wide architectural photography. Headline 'Just listed' / 'Open house', price and key specs (beds/baths/sqft) as `details`, agent contact as footer.",
|
|
130
|
+
},
|
|
131
|
+
"concert-gig": {
|
|
132
|
+
id: "concert-gig",
|
|
133
|
+
label: "Concert / gig poster",
|
|
134
|
+
format: { width: 1240, height: 1754 },
|
|
135
|
+
guidance: "Expressive and genre-driven (grunge, neon, psychedelic, minimal Swiss…). Artist name huge; date, venue, support acts in `details`; 'Tickets' CTA.",
|
|
136
|
+
},
|
|
137
|
+
"fitness-class": {
|
|
138
|
+
id: "fitness-class",
|
|
139
|
+
label: "Fitness class / gym",
|
|
140
|
+
format: { width: 1080, height: 1350 },
|
|
141
|
+
guidance: "Energetic motion, strong athletic subject, bold condensed type. Class name, schedule in `details`, 'First class free'-style CTA.",
|
|
142
|
+
},
|
|
143
|
+
"print-poster": {
|
|
144
|
+
id: "print-poster",
|
|
145
|
+
label: "Print poster (A-series)",
|
|
146
|
+
format: { width: 1754, height: 2480 },
|
|
147
|
+
guidance: "Viewed from distance: headline legible from 3m, strong focal image, keep a 5% margin clear of text for print trim.",
|
|
148
|
+
},
|
|
149
|
+
announcement: {
|
|
150
|
+
id: "announcement",
|
|
151
|
+
label: "Announcement",
|
|
152
|
+
format: { width: 1080, height: 1350 },
|
|
153
|
+
guidance: "One clear message. Short headline, one supporting line, optional CTA.",
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
// ── Design vocabulary the renderer understands ────────────────────────────
|
|
157
|
+
/**
|
|
158
|
+
* Curated families the editor canvas + server render preload
|
|
159
|
+
* (vidspotai-remotion-components CURATED_FONT_FAMILIES). The planner may ONLY
|
|
160
|
+
* pick from these so every poster renders with the intended face.
|
|
161
|
+
*/
|
|
162
|
+
exports.POSTER_FONT_FAMILIES = [
|
|
163
|
+
"Inter",
|
|
164
|
+
"Anton",
|
|
165
|
+
"Bebas Neue",
|
|
166
|
+
"Playfair Display",
|
|
167
|
+
"Caveat",
|
|
168
|
+
"Permanent Marker",
|
|
169
|
+
"IBM Plex Sans",
|
|
170
|
+
"Space Grotesk",
|
|
171
|
+
"Archivo Black",
|
|
172
|
+
"Outfit",
|
|
173
|
+
"Fraunces",
|
|
174
|
+
"DM Serif Display",
|
|
175
|
+
"Poppins",
|
|
176
|
+
];
|
|
177
|
+
exports.PosterFontSchema = zod_1.z.enum(exports.POSTER_FONT_FAMILIES);
|
|
178
|
+
/**
|
|
179
|
+
* Layout archetypes. Each maps to fixed normalized text boxes on the client
|
|
180
|
+
* (poster-studio/agent/poster-layouts.ts), so layout is deterministic and
|
|
181
|
+
* always legible; the planner only chooses which one fits the art.
|
|
182
|
+
*
|
|
183
|
+
* hero-top big headline block in the top ~40%, art below
|
|
184
|
+
* hero-bottom art up top, copy stacked in the bottom ~40%
|
|
185
|
+
* center-stack everything centered over the art (needs a calm center)
|
|
186
|
+
* split-left copy in the left ~45% column, subject on the right
|
|
187
|
+
* split-right mirror of split-left
|
|
188
|
+
* framed copy on a solid panel/band across the lower third
|
|
189
|
+
* minimal-corner small refined copy block anchored bottom-left
|
|
190
|
+
*/
|
|
191
|
+
exports.POSTER_LAYOUT_IDS = [
|
|
192
|
+
"hero-top",
|
|
193
|
+
"hero-bottom",
|
|
194
|
+
"center-stack",
|
|
195
|
+
"split-left",
|
|
196
|
+
"split-right",
|
|
197
|
+
"framed",
|
|
198
|
+
"minimal-corner",
|
|
199
|
+
];
|
|
200
|
+
exports.PosterLayoutSchema = zod_1.z.enum(exports.POSTER_LAYOUT_IDS);
|
|
201
|
+
exports.PosterRenderModeSchema = zod_1.z.enum(["editable", "flat"]);
|
|
202
|
+
const hex = zod_1.z.string().regex(/^#[0-9a-fA-F]{6}$/);
|
|
203
|
+
exports.PosterPaletteSchema = zod_1.z.object({
|
|
204
|
+
/** Fallback canvas fill + scrim/panel base. */
|
|
205
|
+
background: hex,
|
|
206
|
+
/** Headline color. */
|
|
207
|
+
primary: hex,
|
|
208
|
+
/** CTA pill / highlights. */
|
|
209
|
+
accent: hex,
|
|
210
|
+
/** Body/subheadline/details color. */
|
|
211
|
+
text: hex,
|
|
212
|
+
/** Text color on top of the accent (CTA label). */
|
|
213
|
+
onAccent: hex,
|
|
214
|
+
});
|
|
215
|
+
exports.PosterCopySchema = zod_1.z.object({
|
|
216
|
+
headline: zod_1.z.string().min(1).max(80),
|
|
217
|
+
subheadline: zod_1.z.string().max(140).optional(),
|
|
218
|
+
body: zod_1.z.string().max(260).optional(),
|
|
219
|
+
cta: zod_1.z.string().max(32).optional(),
|
|
220
|
+
/** Short scannable lines: date, time, venue, price, specs, roles. */
|
|
221
|
+
details: zod_1.z.array(zod_1.z.string().max(60)).max(5).optional(),
|
|
222
|
+
footer: zod_1.z.string().max(100).optional(),
|
|
223
|
+
});
|
|
224
|
+
exports.PosterVariantPlanSchema = zod_1.z.object({
|
|
225
|
+
/** One-line creative idea, shown to the user on the variant card. */
|
|
226
|
+
concept: zod_1.z.string().max(160),
|
|
227
|
+
layout: exports.PosterLayoutSchema,
|
|
228
|
+
palette: exports.PosterPaletteSchema,
|
|
229
|
+
typography: zod_1.z.object({
|
|
230
|
+
headingFont: exports.PosterFontSchema,
|
|
231
|
+
bodyFont: exports.PosterFontSchema,
|
|
232
|
+
headingCase: zod_1.z.enum(["upper", "title", "sentence"]),
|
|
233
|
+
}),
|
|
234
|
+
copy: exports.PosterCopySchema,
|
|
235
|
+
/** Legibility layer between art and text (editable mode). */
|
|
236
|
+
scrim: zod_1.z.enum(["none", "soft", "strong"]),
|
|
237
|
+
/**
|
|
238
|
+
* Editable: the art prompt, explicitly text-free, describing where the
|
|
239
|
+
* negative space is. Flat: the full poster prompt with the exact copy.
|
|
240
|
+
*/
|
|
241
|
+
imagePrompt: zod_1.z.string().min(10).max(2400),
|
|
242
|
+
negativePrompt: zod_1.z.string().max(400).optional(),
|
|
243
|
+
});
|
|
244
|
+
exports.PosterPlanSchema = zod_1.z.object({
|
|
245
|
+
useCase: exports.PosterUseCaseSchema,
|
|
246
|
+
mode: exports.PosterRenderModeSchema,
|
|
247
|
+
format: zod_1.z.object({ width: zod_1.z.number().int().positive(), height: zod_1.z.number().int().positive() }),
|
|
248
|
+
imageAspect: zod_1.z.enum(exports.POSTER_IMAGE_ASPECTS),
|
|
249
|
+
/** Who it's for + what it must make them do — shown as the agent's brief. */
|
|
250
|
+
audience: zod_1.z.string().max(160),
|
|
251
|
+
goal: zod_1.z.string().max(160),
|
|
252
|
+
tone: zod_1.z.string().max(80),
|
|
253
|
+
variants: zod_1.z.array(exports.PosterVariantPlanSchema).min(1).max(4),
|
|
254
|
+
});
|
|
255
|
+
// ── Requests ───────────────────────────────────────────────────────────────
|
|
256
|
+
exports.PosterAgentRequestSchema = zod_1.z.object({
|
|
257
|
+
prompt: zod_1.z.string().trim().min(3).max(2000),
|
|
258
|
+
/** Absent → the agent infers it from the prompt. */
|
|
259
|
+
useCase: exports.PosterUseCaseSchema.optional(),
|
|
260
|
+
mode: exports.PosterRenderModeSchema.default("editable"),
|
|
261
|
+
variants: zod_1.z.number().int().min(1).max(4).default(2),
|
|
262
|
+
/** Canvas size override (e.g. the user already picked a format). */
|
|
263
|
+
format: zod_1.z
|
|
264
|
+
.object({
|
|
265
|
+
width: zod_1.z.number().int().min(200).max(8000),
|
|
266
|
+
height: zod_1.z.number().int().min(200).max(8000),
|
|
267
|
+
})
|
|
268
|
+
.optional(),
|
|
269
|
+
/** Product photos / references the art should feature. */
|
|
270
|
+
referenceImageUrls: zod_1.z.array(zod_1.z.string().url()).max(4).optional(),
|
|
271
|
+
useBrandKit: zod_1.z.boolean().default(true),
|
|
272
|
+
/** BCP-47. Absent → copy follows the prompt's language. */
|
|
273
|
+
language: zod_1.z.string().max(16).optional(),
|
|
274
|
+
});
|
|
275
|
+
exports.PosterRefineRequestSchema = zod_1.z.object({
|
|
276
|
+
plan: exports.PosterPlanSchema,
|
|
277
|
+
variantIndex: zod_1.z.number().int().min(0).max(3),
|
|
278
|
+
instruction: zod_1.z.string().trim().min(2).max(1000),
|
|
279
|
+
referenceImageUrls: zod_1.z.array(zod_1.z.string().url()).max(4).optional(),
|
|
280
|
+
useBrandKit: zod_1.z.boolean().default(true),
|
|
281
|
+
});
|
|
282
|
+
/** Pick the image aspect closest to a canvas format. */
|
|
283
|
+
function nearestPosterAspect(format) {
|
|
284
|
+
const r = format.width / format.height;
|
|
285
|
+
let best = "3:4";
|
|
286
|
+
let bestDiff = Infinity;
|
|
287
|
+
for (const a of exports.POSTER_IMAGE_ASPECTS) {
|
|
288
|
+
const [w = 1, h = 1] = a.split(":").map(Number);
|
|
289
|
+
const diff = Math.abs(Math.log(r / (w / h)));
|
|
290
|
+
if (diff < bestDiff) {
|
|
291
|
+
bestDiff = diff;
|
|
292
|
+
best = a;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return best;
|
|
296
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { LlmCaller } from "../llmCaller";
|
|
3
|
+
import { PosterAgentRequest, PosterBrandContext, PosterPlan, PosterRenderMode, PosterVariantPlan } from "./posterAgent.schema";
|
|
4
|
+
/**
|
|
5
|
+
* AI Poster Studio — the poster agent's reasoning core. Pure functions over an
|
|
6
|
+
* injected LlmCaller (same shape as covers/coverPlanner.ts) so the Functions
|
|
7
|
+
* controller, scripts, and a no-spend eval can all drive it.
|
|
8
|
+
*
|
|
9
|
+
* planPosterCore prompt → brief + N distinct poster variants
|
|
10
|
+
* refinePosterVariantCore one variant + a plain-language instruction →
|
|
11
|
+
* the updated variant + whether the art must be
|
|
12
|
+
* regenerated (copy/color/layout-only edits in
|
|
13
|
+
* editable mode don't need new art — no spend).
|
|
14
|
+
*
|
|
15
|
+
* The LLM answers into LOOSE schemas; `repairVariant` clamps fonts / colors /
|
|
16
|
+
* lengths into the strict PosterVariantPlanSchema so a slightly-off answer is
|
|
17
|
+
* fixed, not rejected.
|
|
18
|
+
*/
|
|
19
|
+
declare const LooseVariantSchema: z.ZodObject<{
|
|
20
|
+
concept: z.ZodString;
|
|
21
|
+
layout: z.ZodString;
|
|
22
|
+
palette: z.ZodObject<{
|
|
23
|
+
background: z.ZodString;
|
|
24
|
+
primary: z.ZodString;
|
|
25
|
+
accent: z.ZodString;
|
|
26
|
+
text: z.ZodString;
|
|
27
|
+
onAccent: z.ZodString;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
typography: z.ZodObject<{
|
|
30
|
+
headingFont: z.ZodString;
|
|
31
|
+
bodyFont: z.ZodString;
|
|
32
|
+
headingCase: z.ZodString;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
copy: z.ZodObject<{
|
|
35
|
+
headline: z.ZodString;
|
|
36
|
+
subheadline: z.ZodOptional<z.ZodString>;
|
|
37
|
+
body: z.ZodOptional<z.ZodString>;
|
|
38
|
+
cta: z.ZodOptional<z.ZodString>;
|
|
39
|
+
details: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
40
|
+
footer: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
scrim: z.ZodString;
|
|
43
|
+
imagePrompt: z.ZodString;
|
|
44
|
+
negativePrompt: z.ZodOptional<z.ZodString>;
|
|
45
|
+
}, z.core.$strip>;
|
|
46
|
+
type LooseVariant = z.infer<typeof LooseVariantSchema>;
|
|
47
|
+
export declare function repairVariant(v: LooseVariant, mode: PosterRenderMode): PosterVariantPlan;
|
|
48
|
+
export declare function planPosterCore(llm: LlmCaller, args: {
|
|
49
|
+
request: PosterAgentRequest;
|
|
50
|
+
brand?: PosterBrandContext;
|
|
51
|
+
maxRetries?: number;
|
|
52
|
+
}): Promise<PosterPlan>;
|
|
53
|
+
export interface PosterRefineResult {
|
|
54
|
+
variant: PosterVariantPlan;
|
|
55
|
+
/** False → copy/color/layout-only edit; client re-applies without new art. */
|
|
56
|
+
regenerateArt: boolean;
|
|
57
|
+
/** One sentence describing what changed, shown in the agent thread. */
|
|
58
|
+
summary: string;
|
|
59
|
+
}
|
|
60
|
+
export declare function refinePosterVariantCore(llm: LlmCaller, args: {
|
|
61
|
+
plan: PosterPlan;
|
|
62
|
+
variantIndex: number;
|
|
63
|
+
instruction: string;
|
|
64
|
+
brand?: PosterBrandContext;
|
|
65
|
+
hasReferenceImages?: boolean;
|
|
66
|
+
}): Promise<PosterRefineResult>;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=posterPlanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posterPlanner.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/poster/posterPlanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAe,SAAS,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAML,kBAAkB,EAClB,kBAAkB,EAIlB,UAAU,EAEV,gBAAgB,EAEhB,iBAAiB,EAElB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;GAcG;AAIH,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BtB,CAAC;AACH,KAAK,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAkKvD,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,CAyCxF;AAeD,wBAAsB,cAAc,CAClC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;IACJ,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACA,OAAO,CAAC,UAAU,CAAC,CAqDrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,8EAA8E;IAC9E,aAAa,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;IACJ,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GACA,OAAO,CAAC,kBAAkB,CAAC,CAsD7B"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.repairVariant = repairVariant;
|
|
4
|
+
exports.planPosterCore = planPosterCore;
|
|
5
|
+
exports.refinePosterVariantCore = refinePosterVariantCore;
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const logger_1 = require("../../../utils/logger");
|
|
8
|
+
const modelRouter_1 = require("../modelRouter");
|
|
9
|
+
const posterAgent_schema_1 = require("./posterAgent.schema");
|
|
10
|
+
/**
|
|
11
|
+
* AI Poster Studio — the poster agent's reasoning core. Pure functions over an
|
|
12
|
+
* injected LlmCaller (same shape as covers/coverPlanner.ts) so the Functions
|
|
13
|
+
* controller, scripts, and a no-spend eval can all drive it.
|
|
14
|
+
*
|
|
15
|
+
* planPosterCore prompt → brief + N distinct poster variants
|
|
16
|
+
* refinePosterVariantCore one variant + a plain-language instruction →
|
|
17
|
+
* the updated variant + whether the art must be
|
|
18
|
+
* regenerated (copy/color/layout-only edits in
|
|
19
|
+
* editable mode don't need new art — no spend).
|
|
20
|
+
*
|
|
21
|
+
* The LLM answers into LOOSE schemas; `repairVariant` clamps fonts / colors /
|
|
22
|
+
* lengths into the strict PosterVariantPlanSchema so a slightly-off answer is
|
|
23
|
+
* fixed, not rejected.
|
|
24
|
+
*/
|
|
25
|
+
// ── Loose LLM output shapes ───────────────────────────────────────────────
|
|
26
|
+
const LooseVariantSchema = zod_1.z.object({
|
|
27
|
+
concept: zod_1.z.string(),
|
|
28
|
+
layout: zod_1.z.string(),
|
|
29
|
+
palette: zod_1.z.object({
|
|
30
|
+
background: zod_1.z.string(),
|
|
31
|
+
primary: zod_1.z.string(),
|
|
32
|
+
accent: zod_1.z.string(),
|
|
33
|
+
text: zod_1.z.string(),
|
|
34
|
+
onAccent: zod_1.z.string(),
|
|
35
|
+
}),
|
|
36
|
+
typography: zod_1.z.object({
|
|
37
|
+
headingFont: zod_1.z.string(),
|
|
38
|
+
bodyFont: zod_1.z.string(),
|
|
39
|
+
headingCase: zod_1.z.string(),
|
|
40
|
+
}),
|
|
41
|
+
copy: zod_1.z.object({
|
|
42
|
+
headline: zod_1.z.string(),
|
|
43
|
+
subheadline: zod_1.z.string().optional(),
|
|
44
|
+
body: zod_1.z.string().optional(),
|
|
45
|
+
cta: zod_1.z.string().optional(),
|
|
46
|
+
details: zod_1.z.array(zod_1.z.string()).optional(),
|
|
47
|
+
footer: zod_1.z.string().optional(),
|
|
48
|
+
}),
|
|
49
|
+
scrim: zod_1.z.string(),
|
|
50
|
+
imagePrompt: zod_1.z.string(),
|
|
51
|
+
negativePrompt: zod_1.z.string().optional(),
|
|
52
|
+
});
|
|
53
|
+
const LoosePlanSchema = zod_1.z.object({
|
|
54
|
+
useCase: zod_1.z.string(),
|
|
55
|
+
audience: zod_1.z.string(),
|
|
56
|
+
goal: zod_1.z.string(),
|
|
57
|
+
tone: zod_1.z.string(),
|
|
58
|
+
variants: zod_1.z.array(LooseVariantSchema).min(1),
|
|
59
|
+
});
|
|
60
|
+
const LooseRefineSchema = zod_1.z.object({
|
|
61
|
+
variant: LooseVariantSchema,
|
|
62
|
+
regenerateArt: zod_1.z.boolean(),
|
|
63
|
+
summary: zod_1.z.string(),
|
|
64
|
+
});
|
|
65
|
+
// ── Prompt building ───────────────────────────────────────────────────────
|
|
66
|
+
const DESIGN_PRINCIPLES = [
|
|
67
|
+
"You are a senior graphic designer and conversion copywriter who makes posters that work.",
|
|
68
|
+
"Principles you always apply:",
|
|
69
|
+
"- ONE focal point and a 3-level hierarchy: headline → supporting line → details/CTA. Readable in 3 seconds.",
|
|
70
|
+
"- Headlines are short and concrete (benefit, offer, or event name). No filler, no clichés ('Unleash', 'Elevate').",
|
|
71
|
+
"- Copy is REAL and specific to the user's prompt. Never invent facts (prices, dates, addresses, names) the user didn't give — if the design needs one, omit that line instead.",
|
|
72
|
+
"- Contrast: text color must pass WCAG AA against what sits behind it. Pick palettes deliberately; 1 accent color max.",
|
|
73
|
+
"- Type pairing: one display face for the headline, one clean face for body. Condensed/heavy display for energy, serif for elegance/editorial, sans for tech/professional.",
|
|
74
|
+
"- Variants must be genuinely different creative directions (concept, layout, palette), not tweaks of one idea.",
|
|
75
|
+
].join("\n");
|
|
76
|
+
function modeInstructions(mode) {
|
|
77
|
+
if (mode === "editable") {
|
|
78
|
+
return [
|
|
79
|
+
"RENDER MODE: EDITABLE.",
|
|
80
|
+
"The image model renders ONLY the artwork. The copy is placed afterwards as live text layers by the layout you pick.",
|
|
81
|
+
"So `imagePrompt` MUST:",
|
|
82
|
+
"- contain NO text, letters, words, logos, signage, or typography of any kind (say so explicitly: 'no text, no lettering').",
|
|
83
|
+
"- describe the subject, lighting, style, composition, and color palette in rich concrete detail.",
|
|
84
|
+
"- reserve calm negative space exactly where the layout puts the copy: hero-top → empty upper 40%; hero-bottom → empty lower 40%;",
|
|
85
|
+
" center-stack → calm uncluttered center; split-left → subject on the right, left half empty; split-right → mirror;",
|
|
86
|
+
" framed → lower third can be covered by a solid panel; minimal-corner → quiet bottom-left corner.",
|
|
87
|
+
"Put 'text, letters, watermark, logo, signature' in `negativePrompt`.",
|
|
88
|
+
"Choose `scrim`: 'none' if the negative space is already flat and calm, 'soft' for mild texture, 'strong' for busy photography.",
|
|
89
|
+
].join("\n");
|
|
90
|
+
}
|
|
91
|
+
return [
|
|
92
|
+
"RENDER MODE: FLAT.",
|
|
93
|
+
"A typography-capable image model renders the ENTIRE poster, text included, as one image.",
|
|
94
|
+
"So `imagePrompt` MUST be a complete poster brief:",
|
|
95
|
+
"- quote every piece of copy EXACTLY, in double quotes, and say where each sits and how big (e.g. 'large bold condensed headline \"40% OFF\" across the top').",
|
|
96
|
+
"- name the type style (heavy condensed sans / elegant serif …), the palette hex colors, the layout, subject, lighting, and style.",
|
|
97
|
+
"- keep total on-image copy short (≤ ~20 words) — long copy renders badly. Drop body text if needed.",
|
|
98
|
+
"- end with 'professional poster design, crisp legible typography, correct spelling'.",
|
|
99
|
+
"Set `scrim` to 'none' (not used in flat mode). `layout` still describes the composition you asked for.",
|
|
100
|
+
].join("\n");
|
|
101
|
+
}
|
|
102
|
+
function brandLines(brand) {
|
|
103
|
+
if (!brand)
|
|
104
|
+
return "";
|
|
105
|
+
const lines = [];
|
|
106
|
+
if (brand.brandName)
|
|
107
|
+
lines.push(`Brand name: ${brand.brandName}.`);
|
|
108
|
+
const c = brand.colors;
|
|
109
|
+
const colors = [c?.primary, c?.secondary, c?.accent, ...(c?.palette ?? [])].filter(Boolean);
|
|
110
|
+
if (colors.length)
|
|
111
|
+
lines.push(`Brand colors (build the palette from these): ${colors.join(", ")}.`);
|
|
112
|
+
if (brand.fonts?.heading || brand.fonts?.body) {
|
|
113
|
+
lines.push(`Brand fonts: heading "${brand.fonts?.heading ?? "—"}", body "${brand.fonts?.body ?? "—"}" — pick the closest match from the allowed font list.`);
|
|
114
|
+
}
|
|
115
|
+
if (brand.tone)
|
|
116
|
+
lines.push(`Brand voice: ${brand.tone}.`);
|
|
117
|
+
if (brand.avoidPhrases?.length) {
|
|
118
|
+
lines.push(`NEVER use these phrases or claims: ${brand.avoidPhrases.map((p) => `"${p}"`).join(", ")}.`);
|
|
119
|
+
}
|
|
120
|
+
if (brand.hasLogo)
|
|
121
|
+
lines.push("The brand logo will be placed on the poster automatically — don't describe a logo in the art.");
|
|
122
|
+
return lines.length ? `BRAND KIT:\n${lines.join("\n")}` : "";
|
|
123
|
+
}
|
|
124
|
+
function schemaLines() {
|
|
125
|
+
return [
|
|
126
|
+
"Field rules:",
|
|
127
|
+
`- layout: one of ${posterAgent_schema_1.POSTER_LAYOUT_IDS.join(", ")}.`,
|
|
128
|
+
`- typography.headingFont / bodyFont: one of ${posterAgent_schema_1.POSTER_FONT_FAMILIES.join(", ")}.`,
|
|
129
|
+
"- typography.headingCase: upper | title | sentence.",
|
|
130
|
+
"- palette: 6-digit hex colors (#RRGGBB). `onAccent` = text color that reads on `accent`.",
|
|
131
|
+
"- copy.headline ≤ 60 chars; subheadline ≤ 120; body ≤ 220 (often omit); cta ≤ 24 (a verb: 'Shop now', 'Get tickets'); details: up to 4 short lines; footer ≤ 90 (website/handle/address, only if given).",
|
|
132
|
+
"- scrim: none | soft | strong.",
|
|
133
|
+
"- concept: ONE sentence, ≤ 120 chars, naming the creative direction.",
|
|
134
|
+
].join("\n");
|
|
135
|
+
}
|
|
136
|
+
function buildPlanMessages(request, brand, feedback) {
|
|
137
|
+
const useCaseList = posterAgent_schema_1.POSTER_USE_CASE_IDS.map((id) => `- ${id}: ${posterAgent_schema_1.POSTER_USE_CASES[id].label} — ${posterAgent_schema_1.POSTER_USE_CASES[id].guidance}`).join("\n");
|
|
138
|
+
const system = [
|
|
139
|
+
DESIGN_PRINCIPLES,
|
|
140
|
+
"",
|
|
141
|
+
modeInstructions(request.mode),
|
|
142
|
+
"",
|
|
143
|
+
"USE CASES (pick the best fit and follow its guidance):",
|
|
144
|
+
useCaseList,
|
|
145
|
+
"",
|
|
146
|
+
schemaLines(),
|
|
147
|
+
].join("\n");
|
|
148
|
+
const user = [
|
|
149
|
+
`USER PROMPT: ${request.prompt}`,
|
|
150
|
+
request.useCase
|
|
151
|
+
? `USE CASE (fixed by the user): ${request.useCase}.`
|
|
152
|
+
: "USE CASE: infer it from the prompt.",
|
|
153
|
+
`Produce exactly ${request.variants} variant(s).`,
|
|
154
|
+
request.language
|
|
155
|
+
? `Write all copy in this language (BCP-47 "${request.language}").`
|
|
156
|
+
: "Write the copy in the same language as the user's prompt.",
|
|
157
|
+
request.referenceImageUrls?.length
|
|
158
|
+
? `The user attached ${request.referenceImageUrls.length} reference image(s) (e.g. their product). The image model receives them — write the imagePrompt to feature "the product/subject from the reference image" faithfully rather than inventing a different one.`
|
|
159
|
+
: "",
|
|
160
|
+
brandLines(brand),
|
|
161
|
+
feedback ? `\nYOUR PREVIOUS ANSWER HAD PROBLEMS — FIX THESE:\n${feedback}` : "",
|
|
162
|
+
]
|
|
163
|
+
.filter(Boolean)
|
|
164
|
+
.join("\n");
|
|
165
|
+
return [
|
|
166
|
+
{ role: "system", content: system },
|
|
167
|
+
{ role: "user", content: user },
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
// ── Repair ────────────────────────────────────────────────────────────────
|
|
171
|
+
const HEX_RE = /^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/;
|
|
172
|
+
function normHex(v, fallback) {
|
|
173
|
+
const m = (v ?? "").trim().match(HEX_RE);
|
|
174
|
+
if (!m)
|
|
175
|
+
return fallback;
|
|
176
|
+
const raw = m[1] ?? "";
|
|
177
|
+
const full = raw.length === 3 ? raw.split("").map((c) => c + c).join("") : raw;
|
|
178
|
+
return `#${full.toUpperCase()}`;
|
|
179
|
+
}
|
|
180
|
+
function normFont(v, fallback) {
|
|
181
|
+
const key = (v ?? "").toLowerCase().replace(/[^a-z]/g, "");
|
|
182
|
+
return (posterAgent_schema_1.POSTER_FONT_FAMILIES.find((f) => f.toLowerCase().replace(/[^a-z]/g, "") === key) ?? fallback);
|
|
183
|
+
}
|
|
184
|
+
function normEnum(v, allowed, fallback) {
|
|
185
|
+
const s = (v ?? "").trim().toLowerCase();
|
|
186
|
+
return allowed.includes(s) ? s : fallback;
|
|
187
|
+
}
|
|
188
|
+
const clip = (s, n) => {
|
|
189
|
+
const t = (s ?? "").trim();
|
|
190
|
+
return t ? t.slice(0, n) : undefined;
|
|
191
|
+
};
|
|
192
|
+
function repairVariant(v, mode) {
|
|
193
|
+
const details = (v.copy.details ?? [])
|
|
194
|
+
.map((d) => d.trim().slice(0, 60))
|
|
195
|
+
.filter(Boolean)
|
|
196
|
+
.slice(0, 5);
|
|
197
|
+
const negative = mode === "editable"
|
|
198
|
+
? clip([v.negativePrompt, "text, letters, words, typography, watermark, logo, signature"]
|
|
199
|
+
.filter(Boolean)
|
|
200
|
+
.join(", "), 400)
|
|
201
|
+
: clip(v.negativePrompt, 400);
|
|
202
|
+
return posterAgent_schema_1.PosterVariantPlanSchema.parse({
|
|
203
|
+
concept: clip(v.concept, 160) ?? "Poster concept",
|
|
204
|
+
layout: normEnum(v.layout, posterAgent_schema_1.POSTER_LAYOUT_IDS, "hero-top"),
|
|
205
|
+
palette: {
|
|
206
|
+
background: normHex(v.palette.background, "#111111"),
|
|
207
|
+
primary: normHex(v.palette.primary, "#FFFFFF"),
|
|
208
|
+
accent: normHex(v.palette.accent, "#7C3AED"),
|
|
209
|
+
text: normHex(v.palette.text, "#F5F5F5"),
|
|
210
|
+
onAccent: normHex(v.palette.onAccent, "#FFFFFF"),
|
|
211
|
+
},
|
|
212
|
+
typography: {
|
|
213
|
+
headingFont: normFont(v.typography.headingFont, "Anton"),
|
|
214
|
+
bodyFont: normFont(v.typography.bodyFont, "Inter"),
|
|
215
|
+
headingCase: normEnum(v.typography.headingCase, ["upper", "title", "sentence"], "title"),
|
|
216
|
+
},
|
|
217
|
+
copy: {
|
|
218
|
+
headline: clip(v.copy.headline, 80) ?? "Untitled",
|
|
219
|
+
...(clip(v.copy.subheadline, 140) ? { subheadline: clip(v.copy.subheadline, 140) } : {}),
|
|
220
|
+
...(clip(v.copy.body, 260) ? { body: clip(v.copy.body, 260) } : {}),
|
|
221
|
+
...(clip(v.copy.cta, 32) ? { cta: clip(v.copy.cta, 32) } : {}),
|
|
222
|
+
...(details.length ? { details } : {}),
|
|
223
|
+
...(clip(v.copy.footer, 100) ? { footer: clip(v.copy.footer, 100) } : {}),
|
|
224
|
+
},
|
|
225
|
+
scrim: mode === "flat" ? "none" : normEnum(v.scrim, ["none", "soft", "strong"], "soft"),
|
|
226
|
+
imagePrompt: (v.imagePrompt ?? "").trim().slice(0, 2400),
|
|
227
|
+
...(negative ? { negativePrompt: negative } : {}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function variantProblems(v, i, mode) {
|
|
231
|
+
const p = [];
|
|
232
|
+
if (!v.copy.headline?.trim())
|
|
233
|
+
p.push(`variant ${i}: headline is empty.`);
|
|
234
|
+
if ((v.imagePrompt ?? "").trim().length < 40)
|
|
235
|
+
p.push(`variant ${i}: imagePrompt is too thin — describe subject, style, lighting, composition.`);
|
|
236
|
+
if (mode === "flat" && v.copy.headline && !v.imagePrompt.includes(v.copy.headline.trim().slice(0, 12))) {
|
|
237
|
+
p.push(`variant ${i}: flat mode — the imagePrompt must quote the headline exactly.`);
|
|
238
|
+
}
|
|
239
|
+
if (!posterAgent_schema_1.POSTER_LAYOUT_IDS.includes(v.layout))
|
|
240
|
+
p.push(`variant ${i}: unknown layout "${v.layout}".`);
|
|
241
|
+
return p;
|
|
242
|
+
}
|
|
243
|
+
// ── Public API ────────────────────────────────────────────────────────────
|
|
244
|
+
async function planPosterCore(llm, args) {
|
|
245
|
+
const { request, brand } = args;
|
|
246
|
+
const router = new modelRouter_1.ModelRouter();
|
|
247
|
+
const maxRetries = args.maxRetries ?? 1;
|
|
248
|
+
let feedback;
|
|
249
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
250
|
+
const res = await llm.structured({
|
|
251
|
+
model: router.pickFor("planner"),
|
|
252
|
+
fallbackModel: router.fallbackFor("planner"),
|
|
253
|
+
schema: LoosePlanSchema,
|
|
254
|
+
schemaName: "PosterPlan",
|
|
255
|
+
messages: buildPlanMessages(request, brand, feedback),
|
|
256
|
+
temperature: 0.8,
|
|
257
|
+
maxTokens: 6000,
|
|
258
|
+
});
|
|
259
|
+
const loose = res.data;
|
|
260
|
+
const variantsIn = loose.variants.slice(0, request.variants);
|
|
261
|
+
const problems = variantsIn.flatMap((v, i) => variantProblems(v, i, request.mode));
|
|
262
|
+
if (problems.length && attempt < maxRetries) {
|
|
263
|
+
feedback = problems.join("\n");
|
|
264
|
+
logger_1.logger.warn("poster.planner: retrying", { attempt, problems });
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const useCase = request.useCase ??
|
|
268
|
+
normEnum(loose.useCase, posterAgent_schema_1.POSTER_USE_CASE_IDS, "announcement");
|
|
269
|
+
const format = request.format ?? posterAgent_schema_1.POSTER_USE_CASES[useCase].format;
|
|
270
|
+
const plan = posterAgent_schema_1.PosterPlanSchema.parse({
|
|
271
|
+
useCase,
|
|
272
|
+
mode: request.mode,
|
|
273
|
+
format,
|
|
274
|
+
imageAspect: (0, posterAgent_schema_1.nearestPosterAspect)(format),
|
|
275
|
+
audience: clip(loose.audience, 160) ?? "",
|
|
276
|
+
goal: clip(loose.goal, 160) ?? "",
|
|
277
|
+
tone: clip(loose.tone, 80) ?? "",
|
|
278
|
+
variants: variantsIn.map((v) => repairVariant(v, request.mode)),
|
|
279
|
+
});
|
|
280
|
+
logger_1.logger.info("poster.planner: plan accepted", {
|
|
281
|
+
attempt,
|
|
282
|
+
useCase,
|
|
283
|
+
mode: request.mode,
|
|
284
|
+
variants: plan.variants.length,
|
|
285
|
+
imperfect: problems.length > 0,
|
|
286
|
+
});
|
|
287
|
+
return plan;
|
|
288
|
+
}
|
|
289
|
+
// Unreachable: the final attempt always returns above.
|
|
290
|
+
throw new Error("poster planner produced no plan");
|
|
291
|
+
}
|
|
292
|
+
async function refinePosterVariantCore(llm, args) {
|
|
293
|
+
const { plan, variantIndex, instruction, brand } = args;
|
|
294
|
+
const current = plan.variants[variantIndex];
|
|
295
|
+
if (!current)
|
|
296
|
+
throw new Error(`variant ${variantIndex} not found`);
|
|
297
|
+
const router = new modelRouter_1.ModelRouter();
|
|
298
|
+
const system = [
|
|
299
|
+
DESIGN_PRINCIPLES,
|
|
300
|
+
"",
|
|
301
|
+
modeInstructions(plan.mode),
|
|
302
|
+
"",
|
|
303
|
+
schemaLines(),
|
|
304
|
+
"",
|
|
305
|
+
"You are revising ONE existing poster variant based on the user's feedback.",
|
|
306
|
+
"Change only what the feedback asks for (plus anything required to keep the design coherent); keep everything else identical.",
|
|
307
|
+
"Keep `imagePrompt` byte-for-byte identical unless the artwork itself must change.",
|
|
308
|
+
plan.mode === "editable"
|
|
309
|
+
? "Set `regenerateArt` to true ONLY if the artwork itself must change (subject, scene, style, lighting, composition/negative-space position because the layout moved). Copy, colors of text, fonts, CTA, scrim, or layout changes that keep the same negative space do NOT need new art → false."
|
|
310
|
+
: "Flat mode: any visible change requires a re-render, so `regenerateArt` is true unless the feedback asks for nothing visual.",
|
|
311
|
+
"`summary`: one short sentence telling the user what you changed.",
|
|
312
|
+
].join("\n");
|
|
313
|
+
const user = [
|
|
314
|
+
`USE CASE: ${posterAgent_schema_1.POSTER_USE_CASES[plan.useCase].label}. Audience: ${plan.audience}. Goal: ${plan.goal}. Tone: ${plan.tone}.`,
|
|
315
|
+
`CURRENT VARIANT (JSON):\n${JSON.stringify(current, null, 2)}`,
|
|
316
|
+
`USER FEEDBACK: ${instruction}`,
|
|
317
|
+
args.hasReferenceImages ? "Reference image(s) of the user's product/subject are attached to the image model." : "",
|
|
318
|
+
brandLines(brand),
|
|
319
|
+
]
|
|
320
|
+
.filter(Boolean)
|
|
321
|
+
.join("\n\n");
|
|
322
|
+
const res = await llm.structured({
|
|
323
|
+
model: router.pickFor("planner"),
|
|
324
|
+
fallbackModel: router.fallbackFor("planner"),
|
|
325
|
+
schema: LooseRefineSchema,
|
|
326
|
+
schemaName: "PosterRefinement",
|
|
327
|
+
messages: [
|
|
328
|
+
{ role: "system", content: system },
|
|
329
|
+
{ role: "user", content: user },
|
|
330
|
+
],
|
|
331
|
+
temperature: 0.4,
|
|
332
|
+
maxTokens: 3000,
|
|
333
|
+
});
|
|
334
|
+
const variant = repairVariant(res.data.variant, plan.mode);
|
|
335
|
+
// If the art prompt changed materially the art must follow, whatever the
|
|
336
|
+
// model claimed — otherwise a new prompt would silently never render.
|
|
337
|
+
const promptChanged = variant.imagePrompt.trim() !== current.imagePrompt.trim();
|
|
338
|
+
return {
|
|
339
|
+
variant,
|
|
340
|
+
regenerateArt: res.data.regenerateArt || promptChanged,
|
|
341
|
+
summary: (res.data.summary ?? "").trim().slice(0, 240) || "Updated the poster.",
|
|
342
|
+
};
|
|
343
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vidspotai-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.120",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -13,10 +13,6 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"lib"
|
|
15
15
|
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc -p tsconfig.json",
|
|
18
|
-
"watch": "tsc -p tsconfig.json --watch"
|
|
19
|
-
},
|
|
20
16
|
"dependencies": {
|
|
21
17
|
"@anthropic-ai/sdk": "^0.98.0",
|
|
22
18
|
"@aws-sdk/client-s3": "^3.1119.0",
|
|
@@ -49,5 +45,9 @@
|
|
|
49
45
|
"peerDependencies": {
|
|
50
46
|
"firebase-admin": "^13.5.0",
|
|
51
47
|
"ioredis": "^5.8.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -p tsconfig.json",
|
|
51
|
+
"watch": "tsc -p tsconfig.json --watch"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|