@avocadostudio-ai/orchestrator-core 0.14.0 → 0.15.0
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/dist/chat/chat-pipeline-image.d.ts +169 -3
- package/dist/chat/chat-pipeline-image.js +397 -54
- package/dist/chat/chat-pipeline-shared.d.ts +26 -0
- package/dist/chat/chat-pipeline-shared.js +38 -0
- package/dist/chat/chat-pipeline.d.ts +14 -5
- package/dist/chat/chat-pipeline.js +140 -119
- package/dist/chat/plan-json-schema.d.ts +4 -0
- package/dist/chat/plan-json-schema.js +5 -1
- package/dist/chat/prompts.js +2 -0
- package/dist/chat/provider-routing.d.ts +14 -0
- package/dist/chat/provider-routing.js +20 -0
- package/dist/handler/create-orchestrator.js +4 -1
- package/dist/image/image-helpers.d.ts +24 -1
- package/dist/image/image-helpers.js +159 -5
- package/dist/nlp/deterministic-planner-patches.js +19 -1
- package/dist/telemetry/chat-telemetry.d.ts +9 -0
- package/dist/telemetry/chat-telemetry.js +2 -0
- package/dist/tools/builtins/unsplash-search.js +12 -3
- package/dist/tools/types.d.ts +16 -0
- package/package.json +3 -3
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import type { Logger } from "../logger.ts";
|
|
2
|
-
import { type EditPlan, type PageDoc } from "@avocadostudio-ai/shared";
|
|
2
|
+
import { type EditPlan, type Operation, type PageDoc } from "@avocadostudio-ai/shared";
|
|
3
3
|
import type { PendingImageGeneration } from "../state/session-state.ts";
|
|
4
|
+
import { resolveUnsplashImage } from "../image/image-helpers.ts";
|
|
5
|
+
export declare function setChatImageResolverForTests(fn?: typeof resolveUnsplashImage): void;
|
|
6
|
+
/**
|
|
7
|
+
* Which prop on this block an image belongs in, or null if none does.
|
|
8
|
+
*
|
|
9
|
+
* Used by the last-resort path: the message asked for a photo, the planner
|
|
10
|
+
* produced no image op, so find somewhere on the page to put one. It looked
|
|
11
|
+
* for a literal `imageUrl` prop, which meant that on a site whose hero image
|
|
12
|
+
* field is `backgroundImage` the fallback found nothing and the request
|
|
13
|
+
* quietly did nothing at all.
|
|
14
|
+
*
|
|
15
|
+
* Chrome blocks are excluded: the header and the footer own the logo, and
|
|
16
|
+
* "add a photo" has never meant "replace the logo".
|
|
17
|
+
*/
|
|
18
|
+
export declare function primaryImageKeyFor(block: PageDoc["blocks"][number] | null | undefined): string | null;
|
|
4
19
|
export declare function blockHasImageUrlProp(block: PageDoc["blocks"][number] | null | undefined): block is PageDoc["blocks"][number];
|
|
5
20
|
export declare function parsePath(path: string): Array<string | number>;
|
|
6
21
|
export declare function getValueAtPath(root: unknown, path: string): unknown;
|
|
@@ -13,11 +28,113 @@ export declare function extractReferencedItemIndices(message: string): {
|
|
|
13
28
|
hasConstraint: boolean;
|
|
14
29
|
};
|
|
15
30
|
/**
|
|
16
|
-
* Check whether a block type's schema
|
|
31
|
+
* Check whether a block type's schema declares an image at a given path.
|
|
17
32
|
* E.g. "imageUrl" → true for Hero, "features[0].imageUrl" → false for FeatureGrid.
|
|
33
|
+
*
|
|
34
|
+
* The final key is read against the block's own field metadata rather than
|
|
35
|
+
* compared to the literal `imageUrl`, so a block whose image field is called
|
|
36
|
+
* `backgroundImage` or `photo` answers for it too.
|
|
18
37
|
*/
|
|
19
38
|
export declare function blockSupportsImageAtPath(blockType: string, imagePath: string): boolean;
|
|
20
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Every path inside `value` that holds an image URL.
|
|
41
|
+
*
|
|
42
|
+
* Pass `blockType` and the block's declared `kind: "image"` fields are what
|
|
43
|
+
* counts; without it — a patch whose target is unknown, or a block that ships
|
|
44
|
+
* no field metadata — only the built-in `imageUrl` name is recognised, which
|
|
45
|
+
* is what this did for every caller before the block type was threaded in.
|
|
46
|
+
*/
|
|
47
|
+
export declare function detectImagePaths(value: unknown, opts?: {
|
|
48
|
+
blockType?: string;
|
|
49
|
+
}): Set<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Is the field the user has selected in the preview an image field?
|
|
52
|
+
*
|
|
53
|
+
* The overlay reports the path it marked — `imageUrl`, `cards[2].photo` — and
|
|
54
|
+
* the pipeline used to compare it to the string "imageUrl", so selecting the
|
|
55
|
+
* image on any block that names the field differently read as "the user is not
|
|
56
|
+
* looking at an image".
|
|
57
|
+
*/
|
|
58
|
+
export declare function isImageEditablePath(blockType: string | undefined, editablePath: string | undefined): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Where the alt text for the image at `imagePath` belongs.
|
|
61
|
+
*
|
|
62
|
+
* Built-in blocks pair `imageUrl` with `imageAlt`, which is why the pairing
|
|
63
|
+
* used to be a string replace — but a Gallery item pairs `imageUrl` with
|
|
64
|
+
* `alt`, so that replace produced a key the Gallery does not have. The
|
|
65
|
+
* registry knows the pairing; ask it, and keep the old replace for blocks that
|
|
66
|
+
* declare nothing.
|
|
67
|
+
*/
|
|
68
|
+
export declare function imageAltPathFor(blockType: string | undefined, imagePath: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* A props patch that writes `value` at `path` without collapsing the list the
|
|
71
|
+
* path runs through.
|
|
72
|
+
*
|
|
73
|
+
* `update_props` merges a list entry by entry and keeps the *patch's* length,
|
|
74
|
+
* so the obvious construction — `setValueAtPath({}, "cards[0].imageUrl", url)`
|
|
75
|
+
* → `{ cards: [{ imageUrl: url }] }` — takes a three-card grid down to one
|
|
76
|
+
* card. The list has to be rebuilt at the length it currently has, with every
|
|
77
|
+
* entry but the addressed one left empty for the merge to fill back in.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildImagePatchProps(blockProps: Record<string, unknown>, path: string, value: unknown): Record<string, unknown>;
|
|
80
|
+
/**
|
|
81
|
+
* Write `value` at `path` in a patch that is being edited in place, without
|
|
82
|
+
* shortening the list the path runs through.
|
|
83
|
+
*
|
|
84
|
+
* `setValueAtPath` alone creates the list it needs and no more, so writing
|
|
85
|
+
* `cards[0].imageUrl` into a patch that says nothing about `cards` produces a
|
|
86
|
+
* one-element array — and update_props keeps the patch's length, so applying it
|
|
87
|
+
* deletes every other card. That is not hypothetical: a turn asking for the
|
|
88
|
+
* first card's image planned an unrelated `{ title }` patch, the image pass
|
|
89
|
+
* added `cards[0].imageUrl` to it, and a three-card grid came back with one
|
|
90
|
+
* card in it.
|
|
91
|
+
*
|
|
92
|
+
* A patch that already carries the list is left at the length it states — a
|
|
93
|
+
* plan replacing three cards with two means it.
|
|
94
|
+
*/
|
|
95
|
+
export declare function setImageInPatch(patch: Record<string, unknown>, blockProps: Record<string, unknown>, path: string, value: unknown): void;
|
|
96
|
+
/**
|
|
97
|
+
* Every place one op writes an image, and how to write a different image there.
|
|
98
|
+
*
|
|
99
|
+
* The deferred image passes used to ask a plan a single question — "is
|
|
100
|
+
* `patch.imageUrl` a placeholder?" — which is only ever true of an
|
|
101
|
+
* `update_props` op on a block whose image is a top-level prop. A card's image
|
|
102
|
+
* is neither of those things. The planner addresses one entry of a list with
|
|
103
|
+
* `update_item`, and the `update_props` form of the same edit nests the path
|
|
104
|
+
* (`cards[0].imageUrl`); both were invisible to those passes. So a turn that
|
|
105
|
+
* searched Unsplash twice, got two photos back and reported that it had swapped
|
|
106
|
+
* one in had nowhere to put either of them, and the placeholder the planner is
|
|
107
|
+
* told to write stayed on the page.
|
|
108
|
+
*
|
|
109
|
+
* `rewrite` is part of the site rather than left to the caller because the op
|
|
110
|
+
* that writes the image back is not the same shape as the op that asked for it
|
|
111
|
+
* — see `buildImagePatchProps` for the list-length trap it avoids.
|
|
112
|
+
*/
|
|
113
|
+
export type ImagePatchSite = {
|
|
114
|
+
blockId: string;
|
|
115
|
+
pageSlug: string;
|
|
116
|
+
/** The block being patched, when the page state knows it. */
|
|
117
|
+
blockType: string | undefined;
|
|
118
|
+
/** The image's path, relative to this op's patch. */
|
|
119
|
+
path: string;
|
|
120
|
+
/** Where this image's alt text belongs, relative to this op's patch. */
|
|
121
|
+
altPath: string;
|
|
122
|
+
/** What the op writes at `path` today. */
|
|
123
|
+
value: string;
|
|
124
|
+
/** What the op writes at `altPath` today, when it writes anything. */
|
|
125
|
+
alt: string | undefined;
|
|
126
|
+
/** An op writing `url` (and `alt`, when given) to the same place. */
|
|
127
|
+
rewrite: (url: string, alt?: string) => Operation;
|
|
128
|
+
};
|
|
129
|
+
export declare function findImagePatchSites(op: Operation,
|
|
130
|
+
/** The page as it stands now — a lookup, so the caller needs no op narrowing of its own. */
|
|
131
|
+
pageAt: (slug: string) => PageDoc | null | undefined): ImagePatchSite[];
|
|
132
|
+
/**
|
|
133
|
+
* Do two ops address the same image? Block identity is enough for
|
|
134
|
+
* `update_props`; an item op also has to agree on which entry of which list,
|
|
135
|
+
* or a plan touching two cards would patch the wrong one.
|
|
136
|
+
*/
|
|
137
|
+
export declare function opsAddressSameTarget(a: Operation, b: Operation): boolean;
|
|
21
138
|
export declare function imageQueryFromItem(item: Record<string, unknown>, sectionContext?: string): string;
|
|
22
139
|
export declare function shouldPopulateAllChildImages(message: string): boolean;
|
|
23
140
|
export declare function findImageTargets(args: {
|
|
@@ -25,11 +142,29 @@ export declare function findImageTargets(args: {
|
|
|
25
142
|
currentPage: PageDoc;
|
|
26
143
|
targetBlock: PageDoc["blocks"][number];
|
|
27
144
|
patchCandidate: Record<string, unknown>;
|
|
145
|
+
/**
|
|
146
|
+
* The op's `imageQuery` — what the planner said the picture should show.
|
|
147
|
+
* It becomes this block's default subject; a list item that names its own
|
|
148
|
+
* subject (a card's title, an indexed "third one: a dog") still wins, because
|
|
149
|
+
* that is more specific than one phrase for the whole block.
|
|
150
|
+
*/
|
|
151
|
+
plannerQuery?: string;
|
|
28
152
|
}): {
|
|
29
153
|
path: string;
|
|
30
154
|
altPath: string;
|
|
31
155
|
query: string;
|
|
32
156
|
}[];
|
|
157
|
+
/**
|
|
158
|
+
* The image subject the planner stated anywhere in this plan.
|
|
159
|
+
*
|
|
160
|
+
* The two hero fallbacks below fire when no op yielded an image target — the
|
|
161
|
+
* plan named a block this page does not have, or patched nothing an image
|
|
162
|
+
* could hang on. The request was still about an image and the planner still
|
|
163
|
+
* said what it should show, so that answer beats re-deriving one from the
|
|
164
|
+
* sentence. Ops are checked in order and the first stated subject wins; a plan
|
|
165
|
+
* that resolves several images by this route is not a case worth guessing at.
|
|
166
|
+
*/
|
|
167
|
+
export declare function statedImageQuery(plan: EditPlan): string | undefined;
|
|
33
168
|
export declare function rewriteAddBlockToChildImageUpdate(args: {
|
|
34
169
|
plan: EditPlan;
|
|
35
170
|
message: string;
|
|
@@ -76,6 +211,37 @@ export declare function resolveHeroImageForCreatePage(args: {
|
|
|
76
211
|
alt: string;
|
|
77
212
|
source: "ai-generated" | "gdrive" | "unsplash";
|
|
78
213
|
} | null>;
|
|
214
|
+
/**
|
|
215
|
+
* Is this patch all structure and no content — nothing but empty objects and
|
|
216
|
+
* empty lists of them?
|
|
217
|
+
*
|
|
218
|
+
* `{}` is obviously nothing. `{ cards: [{}, {}, {}] }` is the same nothing
|
|
219
|
+
* wearing the shape a list patch has to wear to keep its length, and the ops
|
|
220
|
+
* engine skips it as an unchanged value. Callers that need to know whether
|
|
221
|
+
* stripping a patch left anything behind have to ask the second question too.
|
|
222
|
+
*/
|
|
223
|
+
export declare function isVacuousPatch(value: unknown): boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Rewrite an `update_item` that sets an image into the nested `update_props`
|
|
226
|
+
* form, so the image passes can see it at all.
|
|
227
|
+
*
|
|
228
|
+
* `detectImageOps` and `withUnsplashHeroImage` both walk `update_props` ops and
|
|
229
|
+
* read image paths out of the block's merged props — `cards[0].imageUrl`. An
|
|
230
|
+
* item op carries exactly that edit in a shape neither reads. For a planner
|
|
231
|
+
* with image tools to call that only costs a detour; for one without them —
|
|
232
|
+
* OpenAI defers no image tools, and it is the default planner — those two
|
|
233
|
+
* passes are the *only* route to a real photo, so the stand-in the prompt tells
|
|
234
|
+
* the model to write was the final answer. A card kept `/hero-generated.svg`
|
|
235
|
+
* under a summary saying a photo had been found.
|
|
236
|
+
*
|
|
237
|
+
* Only item ops that actually set an image are rewritten. An item op changing
|
|
238
|
+
* text keeps its shape, which addresses the entry far more precisely.
|
|
239
|
+
*/
|
|
240
|
+
export declare function rewriteItemImageUpdateToProps(args: {
|
|
241
|
+
plan: EditPlan;
|
|
242
|
+
currentPage: PageDoc;
|
|
243
|
+
slug: string;
|
|
244
|
+
}): EditPlan;
|
|
79
245
|
export declare function detectImageOps(args: {
|
|
80
246
|
plan: EditPlan;
|
|
81
247
|
message: string;
|