@avocadostudio-ai/orchestrator-core 0.20.0 → 0.21.1
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.d.ts +3 -1
- package/dist/chat/chat-pipeline.js +6 -1
- package/dist/chat/planner.d.ts +2 -1
- package/dist/chat/planner.js +4 -15
- package/dist/http/chat-stream.d.ts +4 -2
- package/dist/http/chat-stream.js +1 -0
- package/dist/nlp/intent-detection.d.ts +10 -0
- package/dist/nlp/intent-detection.js +46 -0
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type EditPlan, type Operation, type PageDoc } from "@avocadostudio-ai/shared";
|
|
2
2
|
import { type ChatPipelineContext } from "./chat-pipeline-shared.ts";
|
|
3
|
-
import { type ChatRequestBody, type ChatResult } from "../nlp/intent-detection.ts";
|
|
3
|
+
import { type ChatRequestBody, type ChatResult, type RequestScope } from "../nlp/intent-detection.ts";
|
|
4
4
|
import { type ImageSourcePreference } from "../state/session-state.ts";
|
|
5
5
|
import { type SkippedOperation } from "../ops/ops-engine.ts";
|
|
6
6
|
import { demoPlanFromMessage } from "../nlp/deterministic-planner.ts";
|
|
@@ -99,6 +99,8 @@ export declare function runChatPipeline(ctx: ChatPipelineContext, body: ChatRequ
|
|
|
99
99
|
onRollbackDone?: (event: {
|
|
100
100
|
restoredVersion: number;
|
|
101
101
|
}) => void;
|
|
102
|
+
/** What the turn is about, sent once before planning starts. See `resolveRequestScope`. */
|
|
103
|
+
onScope?: (event: RequestScope) => void;
|
|
102
104
|
signal?: AbortSignal;
|
|
103
105
|
}): Promise<{
|
|
104
106
|
code: number;
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { keyFileHint } from "../env-file-location.js";
|
|
3
3
|
import { blockManifestSchema, getImageFieldMap } from "@avocadostudio-ai/shared";
|
|
4
4
|
import { GENERATING_IMAGE_PLACEHOLDER, SEARCHING_IMAGE_PLACEHOLDER, isUnresolvedImageValue, cleanupImagePlaceholders, buildPageDirectory, isVariationRequestMessage, variationVerbIntent, resolveEffectiveSlug, throwIfCanceled, raceCancel, sleepMs, suppressCancelOnly } from "./chat-pipeline-shared.js";
|
|
5
|
-
import { siteCapabilitiesSchema, isBatchAddRequest, isDuplicateBlockRequest, isBlockCatalogQuery, isInfoQuery, isContentQuery, isPageListQuery, requestsPlanFirst, plannerMessageWithPendingContext, buildSiteContextBlock, infoResponse } from "../nlp/intent-detection.js";
|
|
5
|
+
import { resolveRequestScope, siteCapabilitiesSchema, isBatchAddRequest, isDuplicateBlockRequest, isBlockCatalogQuery, isInfoQuery, isContentQuery, isPageListQuery, requestsPlanFirst, plannerMessageWithPendingContext, buildSiteContextBlock, infoResponse } from "../nlp/intent-detection.js";
|
|
6
6
|
import { isLikelyClarificationFollowUp } from "../nlp/intent-helpers.js";
|
|
7
7
|
import { versions, pendingClarificationBySession, chatHistoryBySession, continuationChainBySession, imageSourcePreferenceBySession, getSessionDraft, getPage, setPage, pushUndo, bumpVersion, pushRecentEdit, pushVersionEntry, pushChatHistory, schedulePersistState, removePage, getSuggestionMemory, recordSuggestionsShown, recordSuggestionClicked } from "../state/session-state.js";
|
|
8
8
|
import { getSiteAssets } from "../state/site-assets.js";
|
|
@@ -291,6 +291,11 @@ export async function runChatPipeline(ctx, body, options) {
|
|
|
291
291
|
if (!body.session || !body.slug || (requiresMessage && !body.message)) {
|
|
292
292
|
return { code: 400, payload: { error: "session and slug are required; message is required for planning" } };
|
|
293
293
|
}
|
|
294
|
+
// Only a typed turn has a message to read the scope from. Approving a held
|
|
295
|
+
// plan or continuing a chain says nothing about it, so the editor falls back
|
|
296
|
+
// to following the ops as they land.
|
|
297
|
+
if (requiresMessage)
|
|
298
|
+
options?.onScope?.(resolveRequestScope(body));
|
|
294
299
|
// An explicit "make a plan first" / "plan it before doing" request now APPLIES
|
|
295
300
|
// the plan to the draft as a revertible soft draft instead of holding it
|
|
296
301
|
// uncommitted: the user reviews the whole page in the live preview and Keeps or
|
package/dist/chat/planner.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type BlockManifest, type EditPlan, type Operation, type PageDoc } from
|
|
|
2
2
|
/** Reset the singleton (useful for tests that swap API keys). */
|
|
3
3
|
export declare function resetOpenAIClient(): void;
|
|
4
4
|
import { type ParsedIntent, blockContractsSummary, pageMetaContractSummary, plannerContextPack } from "../nlp/deterministic-planner.ts";
|
|
5
|
+
import { isPageWideTranslationRequest } from "../nlp/intent-detection.ts";
|
|
5
6
|
import { type TokenUsage } from "../telemetry/usage.ts";
|
|
6
7
|
import type { ToolRuntime } from "../tools/runtime.ts";
|
|
7
8
|
import type { ToolExecutionEvent } from "../tools/types.ts";
|
|
@@ -13,7 +14,7 @@ export declare const PlannerOutputError: typeof PlannerError;
|
|
|
13
14
|
/** @deprecated Use `isPlannerError` instead. */
|
|
14
15
|
export declare const isPlannerOutputError: typeof isPlannerError;
|
|
15
16
|
export declare function isChatStrictPrimaryOpMode(): boolean;
|
|
16
|
-
export
|
|
17
|
+
export { isPageWideTranslationRequest };
|
|
17
18
|
export type PlannerContractMode = "minimal" | "targeted" | "full";
|
|
18
19
|
export type PlannerSchemaContextMeta = {
|
|
19
20
|
contractMode: PlannerContractMode;
|
package/dist/chat/planner.js
CHANGED
|
@@ -18,7 +18,7 @@ export function resetOpenAIClient() {
|
|
|
18
18
|
_openaiSingleton = null;
|
|
19
19
|
}
|
|
20
20
|
import { extractAudienceTarget, blockContractsSummary, fetchImageAsBase64, resolveAttachmentsForLlm, pageMetaContractSummary, intentSchema } from "../nlp/deterministic-planner.js";
|
|
21
|
-
import { isBatchAddRequest, isBatchRemoveRequest, isBatchReorderRequest, isPageWideRewriteRequest, isDuplicateAndModifyRequest, requestsPlanFirst } from "../nlp/intent-detection.js";
|
|
21
|
+
import { isBatchAddRequest, isBatchRemoveRequest, isBatchReorderRequest, isPageWideRewriteRequest, isPageWideTranslationRequest, isDuplicateAndModifyRequest, requestsPlanFirst } from "../nlp/intent-detection.js";
|
|
22
22
|
import { extractJsonObject, inferBlockTypeFromText, normalizePlanCandidate, repairAndParseJsonWithMeta } from "../nlp/plan-normalizer.js";
|
|
23
23
|
import { extractUsage, ZERO_USAGE } from "../telemetry/usage.js";
|
|
24
24
|
import { intentJsonSchema } from "./plan-json-schema.js";
|
|
@@ -76,20 +76,9 @@ function extractResponsesOutcome(response) {
|
|
|
76
76
|
export function isChatStrictPrimaryOpMode() {
|
|
77
77
|
return envFlag("CHAT_STRICT_PRIMARY_OP_MODE", false);
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/\btranslation\b/.test(lower) ||
|
|
83
|
-
/\blocaliz/.test(lower) ||
|
|
84
|
-
/\bgerman\b/.test(lower) ||
|
|
85
|
-
/\bdeutsch\b/.test(lower);
|
|
86
|
-
if (!asksTranslation)
|
|
87
|
-
return false;
|
|
88
|
-
return (/\b(this|the|entire|whole|full)\s+(\w+\s+)*page\b/.test(lower) ||
|
|
89
|
-
/\bwhole\s+site\b/.test(lower) ||
|
|
90
|
-
/\ball\s+sections?\b/.test(lower) ||
|
|
91
|
-
/\btranslate\s+(\w+\s+)*page\b/.test(lower));
|
|
92
|
-
}
|
|
79
|
+
// Moved to intent-detection so the request-scope resolver can use it without
|
|
80
|
+
// an import cycle; re-exported for the planners that import it from here.
|
|
81
|
+
export { isPageWideTranslationRequest };
|
|
93
82
|
const EDIT_PLAN_SHAPE_HINT = {
|
|
94
83
|
intent: "edit_plan | needs_clarification",
|
|
95
84
|
summary_for_user: "string",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type ChatPipelineContext } from "../chat/chat-pipeline.ts";
|
|
2
|
-
import type { ChatRequestBody } from "../nlp/intent-detection.ts";
|
|
3
|
-
export type ChatStreamEvent = {
|
|
2
|
+
import type { ChatRequestBody, RequestScope } from "../nlp/intent-detection.ts";
|
|
3
|
+
export type ChatStreamEvent = ({
|
|
4
|
+
type: "scope";
|
|
5
|
+
} & RequestScope) | {
|
|
4
6
|
type: "token";
|
|
5
7
|
text: string;
|
|
6
8
|
} | {
|
package/dist/http/chat-stream.js
CHANGED
|
@@ -29,6 +29,7 @@ export async function runChatStream(ctx, body, options) {
|
|
|
29
29
|
try {
|
|
30
30
|
const result = await runChatPipeline(ctx, body, {
|
|
31
31
|
signal,
|
|
32
|
+
onScope: (scope) => emit({ type: "scope", ...scope }),
|
|
32
33
|
onPlanningToken: (token) => emit({ type: "token", text: token }),
|
|
33
34
|
onThinking: (event) => {
|
|
34
35
|
if (event.type === "start")
|
|
@@ -281,6 +281,16 @@ export declare function isPageListQuery(message: string): boolean;
|
|
|
281
281
|
* pages or clarify. See planner-quality issue #29.
|
|
282
282
|
*/
|
|
283
283
|
export declare function hasCrossPageScope(message: string): boolean;
|
|
284
|
+
export declare function isPageWideTranslationRequest(message: string): boolean;
|
|
285
|
+
export type RequestScope = {
|
|
286
|
+
scope: "block";
|
|
287
|
+
blockId: string;
|
|
288
|
+
} | {
|
|
289
|
+
scope: "page";
|
|
290
|
+
} | {
|
|
291
|
+
scope: "site";
|
|
292
|
+
};
|
|
293
|
+
export declare function resolveRequestScope(body: Pick<ChatRequestBody, "message" | "activeBlockId">): RequestScope;
|
|
284
294
|
export declare function isContentQuery(message: string): boolean;
|
|
285
295
|
export type PageObservation = {
|
|
286
296
|
/** What is true, stated so a reader can check it. */
|
|
@@ -302,6 +302,52 @@ export function isPageListQuery(message) {
|
|
|
302
302
|
export function hasCrossPageScope(message) {
|
|
303
303
|
return CROSS_PAGE_SCOPE_PATTERN.test(normalizeForIntent(stripSiteContextEnvelope(message)));
|
|
304
304
|
}
|
|
305
|
+
export function isPageWideTranslationRequest(message) {
|
|
306
|
+
const lower = message.toLowerCase();
|
|
307
|
+
const asksTranslation = /\btranslate\b/.test(lower) ||
|
|
308
|
+
/\btranslation\b/.test(lower) ||
|
|
309
|
+
/\blocaliz/.test(lower) ||
|
|
310
|
+
/\bgerman\b/.test(lower) ||
|
|
311
|
+
/\bdeutsch\b/.test(lower);
|
|
312
|
+
if (!asksTranslation)
|
|
313
|
+
return false;
|
|
314
|
+
return (/\b(this|the|entire|whole|full)\s+(\w+\s+)*page\b/.test(lower) ||
|
|
315
|
+
/\bwhole\s+site\b/.test(lower) ||
|
|
316
|
+
/\ball\s+sections?\b/.test(lower) ||
|
|
317
|
+
/\btranslate\s+(\w+\s+)*page\b/.test(lower));
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* The message names the page, or several blocks at once, as what it is about.
|
|
321
|
+
* English and German, since the editor ships both.
|
|
322
|
+
*/
|
|
323
|
+
const PAGE_REFERENCE_PATTERN = /\b(?:this|the|whole|entire|full|current|my|our)\s+page\b|\bpage[\s-]wide\b|\b(?:all|every|each)\s+(?:the\s+|of\s+the\s+)?(?:sections?|blocks?)\b|\b(?:diese|die|ganze|gesamte|komplette|meine|unsere)\s+seite\b|\balle\s+(?:abschnitte|bl[öo]cke|sektionen)\b/;
|
|
324
|
+
/*
|
|
325
|
+
* What a chat turn is about, decided before any model is called.
|
|
326
|
+
*
|
|
327
|
+
* The editor shows it while the turn runs — a sweep over the block being
|
|
328
|
+
* worked on, or a frame round the whole page. It used to guess from the
|
|
329
|
+
* selection alone, so "review this page" with the hero selected spent the
|
|
330
|
+
* whole turn shimmering the hero: a review writes no ops, so the stream never
|
|
331
|
+
* named anything better. The selection is what the user was looking at, not
|
|
332
|
+
* necessarily what they asked about; it counts only when the message names
|
|
333
|
+
* nothing wider.
|
|
334
|
+
*/
|
|
335
|
+
export function resolveRequestScope(body) {
|
|
336
|
+
const message = stripSiteContextEnvelope(body.message ?? "");
|
|
337
|
+
if (hasCrossPageScope(message))
|
|
338
|
+
return { scope: "site" };
|
|
339
|
+
const m = normalizeForIntent(message);
|
|
340
|
+
if (PAGE_REFERENCE_PATTERN.test(m) ||
|
|
341
|
+
isPageWideRewriteRequest(message) ||
|
|
342
|
+
isPageWideTranslationRequest(message) ||
|
|
343
|
+
isBatchAddRequest(message) ||
|
|
344
|
+
isBatchRemoveRequest(message) ||
|
|
345
|
+
isBatchReorderRequest(message)) {
|
|
346
|
+
return { scope: "page" };
|
|
347
|
+
}
|
|
348
|
+
const blockId = body.activeBlockId?.trim();
|
|
349
|
+
return blockId ? { scope: "block", blockId } : { scope: "page" };
|
|
350
|
+
}
|
|
305
351
|
export function isContentQuery(message) {
|
|
306
352
|
const m = normalizeForIntent(message);
|
|
307
353
|
// Exclude messages with clear edit/mutation verbs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avocadostudio-ai/orchestrator-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./package.json": "./package.json",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"openai": "^4.87.1",
|
|
23
23
|
"sharp": "^0.35.4",
|
|
24
24
|
"zod": "^4.3.6",
|
|
25
|
-
"@avocadostudio-ai/
|
|
26
|
-
"@avocadostudio-ai/
|
|
25
|
+
"@avocadostudio-ai/migration-sdk": "^0.21.1",
|
|
26
|
+
"@avocadostudio-ai/shared": "^0.21.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@anthropic-ai/claude-agent-sdk": "^0.3.220",
|