@paroicms/site-generator-plugin 0.9.0 → 0.10.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/gen-backend/ddl/site-generator.ddl.sql +57 -9
- package/gen-backend/dist/commands/execute-command.js +35 -9
- package/gen-backend/dist/commands/generator-session.js +49 -10
- package/gen-backend/dist/data-format.js +32 -4
- package/gen-backend/dist/db/db-init.js +3 -1
- package/gen-backend/dist/db/db-read.queries.js +142 -0
- package/gen-backend/dist/db/db-write.queries.js +144 -0
- package/gen-backend/dist/db/ddl-migration.js +8 -6
- package/gen-backend/dist/db/formatters.js +46 -0
- package/gen-backend/dist/generator/fake-content-generator.ts/content-report.js +9 -5
- package/gen-backend/dist/generator/fake-content-generator.ts/create-database-with-fake-content.js +18 -13
- package/gen-backend/dist/generator/fake-content-generator.ts/generate-fake-content.js +16 -12
- package/gen-backend/dist/generator/fake-content-generator.ts/invoke-generate-fake-content.js +26 -17
- package/gen-backend/dist/generator/lib/calling-llm-anthropic.js +33 -0
- package/gen-backend/dist/generator/lib/calling-llm-mistral.js +156 -0
- package/gen-backend/dist/generator/lib/create-prompt.js +2 -2
- package/gen-backend/dist/generator/lib/debug-utils.js +74 -48
- package/gen-backend/dist/generator/lib/llm-tokens.js +7 -9
- package/gen-backend/dist/generator/lib/llm-utils.js +8 -0
- package/gen-backend/dist/generator/lib/prompt-template.js +10 -0
- package/gen-backend/dist/generator/lib/session-utils.js +31 -0
- package/gen-backend/dist/generator/llm-queries/invoke-message-guard.js +20 -9
- package/gen-backend/dist/generator/llm-queries/invoke-new-site-analysis.js +73 -47
- package/gen-backend/dist/generator/llm-queries/invoke-update-site-schema.js +106 -43
- package/gen-backend/dist/generator/site-generator/site-generator.js +26 -18
- package/gen-backend/dist/lib/create-raw-context.js +31 -0
- package/gen-backend/dist/lib/site-remover.js +1 -1
- package/gen-backend/dist/plugin.js +8 -54
- package/gen-backend/prompts/generate-fake-content-multiple-documents.md +5 -5
- package/gen-backend/prompts/generate-fake-content-multiple-parts.md +5 -5
- package/gen-backend/prompts/generate-fake-content-single.md +4 -4
- package/gen-backend/prompts/{new-site-1-analysis.md → initial-1-analysis.md} +38 -29
- package/gen-backend/prompts/{new-site-2-fields.md → initial-2-fields.md} +3 -3
- package/gen-backend/prompts/message-guard.md +1 -1
- package/gen-backend/prompts/update-site-schema-1-write-details.md +5 -5
- package/gen-backend/prompts/update-site-schema-2-execute.md +29 -29
- package/gen-front/dist/gen-front.css +1 -1
- package/gen-front/dist/gen-front.mjs +137 -1175
- package/package.json +30 -32
- package/gen-backend/dist/commands/actions.js +0 -49
- package/gen-backend/dist/db/db.queries.js +0 -60
- package/gen-backend/dist/errors.js +0 -20
- package/gen-backend/dist/generator/actions.js +0 -45
- package/gen-backend/dist/generator/fake-content-generator.ts/augment-fields.js +0 -51
- package/gen-backend/dist/generator/generator-session.js +0 -33
- package/gen-backend/dist/generator/generator-types.js +0 -1
- package/gen-backend/dist/generator/lib/token-tracking.js +0 -118
- package/gen-backend/dist/generator/session/generator-session.js +0 -33
- package/gen-backend/dist/generator/session/session-command.js +0 -17
- package/gen-backend/dist/generator/site-generator/theme-scss.js +0 -262
- package/gen-backend/dist/lib/generator-context.js +0 -14
- package/gen-backend/prompts/test-message1.txt +0 -1
- package/gen-front/dist/gen-front.eot +0 -0
- package/gen-front/dist/gen-front.svg +0 -345
- package/gen-front/dist/gen-front.ttf +0 -0
- package/gen-front/dist/gen-front.woff +0 -0
- package/gen-front/dist/gen-front.woff2 +0 -0
- package/gen-front/dist/gen-front2.woff2 +0 -0
- package/gen-front/dist/gen-front3.woff2 +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function buildPromptTemplate(template) {
|
|
2
|
+
return (variables) => {
|
|
3
|
+
let result = template;
|
|
4
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
5
|
+
const placeholder = `{{${key}}}`;
|
|
6
|
+
result = result.replace(new RegExp(placeholder, "g"), value ?? "");
|
|
7
|
+
}
|
|
8
|
+
return result;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { messageOf } from "@paroi/data-formatters-lib";
|
|
2
|
+
import { insertIssueEvent, updateStep } from "../../db/db-write.queries.js";
|
|
3
|
+
export function safeCallStep(ctx, stepHandle, cb) {
|
|
4
|
+
void safeCallStepExec(ctx, stepHandle, cb);
|
|
5
|
+
}
|
|
6
|
+
async function safeCallStepExec(ctx, stepHandle, cb) {
|
|
7
|
+
const { logger, sessionId } = ctx;
|
|
8
|
+
const { stepNumber } = stepHandle;
|
|
9
|
+
try {
|
|
10
|
+
await cb();
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
logger.error(`[${sessionId}]`, error);
|
|
14
|
+
try {
|
|
15
|
+
await updateStep(ctx, stepHandle, {
|
|
16
|
+
status: "failed",
|
|
17
|
+
currentActivity: null,
|
|
18
|
+
explanation: null, // TODO: implement error explanation (localized)
|
|
19
|
+
});
|
|
20
|
+
await insertIssueEvent(ctx, {
|
|
21
|
+
eventType: "stepError",
|
|
22
|
+
issueMessage: messageOf(error),
|
|
23
|
+
llmTaskName: undefined, // TODO: implement StepError exception with task name
|
|
24
|
+
stepNumber,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.error(`[${sessionId}] Error after error`, error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { boolVal, listValOrUndef, strVal, strValOrUndef, } from "@paroi/data-formatters-lib";
|
|
2
|
-
import {
|
|
2
|
+
import { insertIssueEvent } from "../../db/db-write.queries.js";
|
|
3
|
+
import { invokeClaude } from "../lib/calling-llm-anthropic.js";
|
|
3
4
|
import { createPromptTemplate } from "../lib/create-prompt.js";
|
|
4
5
|
import { debugLlmOutput } from "../lib/debug-utils.js";
|
|
5
6
|
import { parseLlmResponseAsProperties } from "../lib/parse-llm-response.js";
|
|
@@ -8,19 +9,25 @@ const guardPrompt = await createPromptTemplate({
|
|
|
8
9
|
});
|
|
9
10
|
const invalidCauses = new Set(["rude", "malicious", "outOfScope", "technicalLimits", "noSense"]);
|
|
10
11
|
export async function invokeMessageGuard(ctx, input, { skipOutOfScopeCheck = false } = {}) {
|
|
11
|
-
const
|
|
12
|
+
const llmTaskName = "guard";
|
|
12
13
|
const llmInput = {
|
|
13
14
|
message: input.prompt,
|
|
14
15
|
};
|
|
15
|
-
const debug = await debugLlmOutput(ctx,
|
|
16
|
+
const debug = await debugLlmOutput(ctx, llmTaskName, ctx.anthropicModelName, undefined, {
|
|
16
17
|
message: llmInput.message,
|
|
17
18
|
});
|
|
18
|
-
let
|
|
19
|
-
if (!
|
|
20
|
-
const
|
|
21
|
-
|
|
19
|
+
let llmOutput = debug.stored;
|
|
20
|
+
if (!llmOutput) {
|
|
21
|
+
const { messageContent, report } = await invokeClaude(ctx, {
|
|
22
|
+
llmTaskName,
|
|
23
|
+
prompt: guardPrompt(llmInput),
|
|
24
|
+
maxTokens: 200,
|
|
25
|
+
temperature: 0.2,
|
|
26
|
+
systemInstruction: "beFast",
|
|
27
|
+
});
|
|
28
|
+
llmOutput = await debug.getMessageContent(messageContent, report);
|
|
22
29
|
}
|
|
23
|
-
const rawReport = parseLlmResponseAsProperties(
|
|
30
|
+
const rawReport = parseLlmResponseAsProperties(llmOutput.output, [
|
|
24
31
|
{
|
|
25
32
|
tagName: "guard_yaml",
|
|
26
33
|
key: "guard",
|
|
@@ -36,7 +43,11 @@ export async function invokeMessageGuard(ctx, input, { skipOutOfScopeCheck = fal
|
|
|
36
43
|
}
|
|
37
44
|
if (report.valid)
|
|
38
45
|
return;
|
|
39
|
-
await
|
|
46
|
+
await insertIssueEvent(ctx, {
|
|
47
|
+
eventType: "blockedByGuard",
|
|
48
|
+
llmTaskName: "guard",
|
|
49
|
+
issueMessage: report.explanation,
|
|
50
|
+
});
|
|
40
51
|
return {
|
|
41
52
|
success: false,
|
|
42
53
|
language: report.language,
|
|
@@ -1,72 +1,90 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadStep } from "../../db/db-read.queries.js";
|
|
2
|
+
import { insertStep, saveCompletedSchemaStep, updateStep, } from "../../db/db-write.queries.js";
|
|
2
3
|
import { reorderObjectKeys } from "../helpers/js-utils.js";
|
|
4
|
+
import { invokeClaude } from "../lib/calling-llm-anthropic.js";
|
|
3
5
|
import { createPromptTemplate, getPredefinedFields, getSiteSchemaTsDefs, } from "../lib/create-prompt.js";
|
|
4
6
|
import { debugLlmOutput } from "../lib/debug-utils.js";
|
|
5
7
|
import { parseMarkdownBulletedList } from "../lib/markdown-bulleted-list-parser.js";
|
|
6
8
|
import { parseLlmResponseAsProperties } from "../lib/parse-llm-response.js";
|
|
9
|
+
import { safeCallStep } from "../lib/session-utils.js";
|
|
7
10
|
import { createL10n } from "../site-schema-generator/create-l10n.js";
|
|
8
11
|
import { createSiteSchemaFromAnalysis } from "../site-schema-generator/create-site-schema.js";
|
|
9
12
|
import { invokeUpdateSiteSchema } from "./invoke-update-site-schema.js";
|
|
10
13
|
export const analyzePrompt = await createPromptTemplate({
|
|
11
|
-
fileName: "
|
|
14
|
+
fileName: "initial-1-analysis.md",
|
|
12
15
|
withSiteSchemaTsDefs: true,
|
|
13
16
|
});
|
|
14
17
|
const fieldsPrompt = await createPromptTemplate({
|
|
15
|
-
fileName: "
|
|
18
|
+
fileName: "initial-2-fields.md",
|
|
16
19
|
withSiteSchemaTsDefs: true,
|
|
17
20
|
});
|
|
18
|
-
export async function
|
|
19
|
-
const
|
|
21
|
+
export async function startInitialAnalysis(ctx, input) {
|
|
22
|
+
const stepHandle = await insertStep(ctx, {
|
|
23
|
+
kind: "initialSchema",
|
|
24
|
+
status: "pending",
|
|
25
|
+
currentActivity: "initial1",
|
|
26
|
+
});
|
|
27
|
+
safeCallStep(ctx, stepHandle, () => invokeInitialAnalysis(ctx, stepHandle, input));
|
|
28
|
+
return await loadStep(ctx, stepHandle.stepNumber);
|
|
29
|
+
}
|
|
30
|
+
export async function invokeInitialAnalysis(ctx, stepHandle, input) {
|
|
31
|
+
const { analysis, unusedInformation, explanation, llmReport: llmReport1, } = await invokeAnalysisStep1(ctx, input, stepHandle);
|
|
20
32
|
const siteSchema = createSiteSchemaFromAnalysis(analysis);
|
|
21
|
-
|
|
33
|
+
await updateStep(ctx, stepHandle, {
|
|
34
|
+
currentActivity: "initial2",
|
|
35
|
+
explanation: explanation ?? null,
|
|
36
|
+
});
|
|
37
|
+
const { unusedInformation: unusedInformation2, llmReport: llmReport2 } = await invokeAnalysisStep2(ctx, { prompt: createUnusedInformationPrompt(unusedInformation, analysis) ?? "" }, siteSchema, stepHandle);
|
|
22
38
|
reorderSiteSchemaNodeTypes(siteSchema);
|
|
23
39
|
const l10n = createL10n(analysis, siteSchema);
|
|
24
40
|
const siteTitle = {
|
|
25
41
|
[analysis.siteProperties.language]: analysis.siteProperties.title,
|
|
26
42
|
};
|
|
43
|
+
const completedValues = {
|
|
44
|
+
status: "completed",
|
|
45
|
+
siteSchema,
|
|
46
|
+
l10n,
|
|
47
|
+
localizedValues: { siteTitle },
|
|
48
|
+
inputTokenCount: llmReport1.inputTokenCount + llmReport2.inputTokenCount,
|
|
49
|
+
outputTokenCount: (llmReport1.outputTokenCount ?? 0) + (llmReport2.outputTokenCount ?? 0),
|
|
50
|
+
promptTitle: undefined, // TODO: implement prompt title
|
|
51
|
+
};
|
|
27
52
|
if (!unusedInformation2) {
|
|
28
|
-
await
|
|
29
|
-
return
|
|
30
|
-
siteTitle,
|
|
31
|
-
siteSchema,
|
|
32
|
-
l10n,
|
|
33
|
-
changed: true,
|
|
34
|
-
explanation,
|
|
35
|
-
};
|
|
53
|
+
await saveCompletedSchemaStep(ctx, stepHandle, completedValues);
|
|
54
|
+
return;
|
|
36
55
|
}
|
|
37
56
|
ctx.logger.debug("Unused information:", unusedInformation2);
|
|
38
|
-
|
|
57
|
+
await invokeUpdateSiteSchema(ctx, stepHandle, {
|
|
39
58
|
prompt: unusedInformation2,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
}, { asRemainingPrompt: true });
|
|
46
|
-
await updateSession(ctx, { status: "analyzed", promptCountInc: 1 });
|
|
47
|
-
return {
|
|
48
|
-
siteTitle,
|
|
49
|
-
siteSchema: updated.siteSchema,
|
|
50
|
-
l10n: updated.l10n,
|
|
51
|
-
changed: true,
|
|
52
|
-
explanation: updated.changed ? `${explanation}\n\n${updated.explanation}` : explanation,
|
|
53
|
-
};
|
|
59
|
+
fromStepSchema: completedValues,
|
|
60
|
+
}, {
|
|
61
|
+
asRemainingOf: completedValues,
|
|
62
|
+
});
|
|
54
63
|
}
|
|
55
|
-
async function invokeAnalysisStep1(ctx, input) {
|
|
56
|
-
const
|
|
64
|
+
async function invokeAnalysisStep1(ctx, input, stepHandle) {
|
|
65
|
+
const llmTaskName = "analysis-1";
|
|
57
66
|
const llmInput = {
|
|
58
67
|
message: input.prompt,
|
|
59
68
|
siteSchemaTsDefs: getSiteSchemaTsDefs(),
|
|
60
69
|
};
|
|
61
|
-
const debug = await debugLlmOutput(ctx,
|
|
70
|
+
const debug = await debugLlmOutput(ctx, llmTaskName, ctx.anthropicModelName, stepHandle, {
|
|
62
71
|
message: llmInput.message,
|
|
63
72
|
});
|
|
64
|
-
let
|
|
65
|
-
if (!
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
let llmOutput = debug.stored;
|
|
74
|
+
if (!llmOutput) {
|
|
75
|
+
// Create formatted messages using the template
|
|
76
|
+
const prompt = analyzePrompt(llmInput);
|
|
77
|
+
// Call the model
|
|
78
|
+
const { messageContent, report } = await invokeClaude(ctx, {
|
|
79
|
+
llmTaskName,
|
|
80
|
+
prompt,
|
|
81
|
+
maxTokens: 4_000,
|
|
82
|
+
temperature: 0.1,
|
|
83
|
+
systemInstruction: "beSmart",
|
|
84
|
+
});
|
|
85
|
+
llmOutput = await debug.getMessageContent(messageContent, report);
|
|
68
86
|
}
|
|
69
|
-
const rawAnalysis = parseLlmResponseAsProperties(
|
|
87
|
+
const rawAnalysis = parseLlmResponseAsProperties(llmOutput.output, [
|
|
70
88
|
{
|
|
71
89
|
tagName: "website_properties_yaml",
|
|
72
90
|
key: "siteProperties",
|
|
@@ -102,30 +120,38 @@ async function invokeAnalysisStep1(ctx, input) {
|
|
|
102
120
|
};
|
|
103
121
|
return {
|
|
104
122
|
analysis,
|
|
105
|
-
explanation: rawAnalysis.explanation,
|
|
106
123
|
unusedInformation: rawAnalysis.unusedInformation,
|
|
124
|
+
explanation: rawAnalysis.explanation,
|
|
125
|
+
llmReport: llmOutput.llmReport,
|
|
107
126
|
};
|
|
108
127
|
}
|
|
109
128
|
async function invokeAnalysisStep2(ctx, input,
|
|
110
129
|
/** Will be mutated. */
|
|
111
|
-
siteSchema) {
|
|
112
|
-
const
|
|
130
|
+
siteSchema, stepHandle) {
|
|
131
|
+
const llmTaskName = "analysis-2";
|
|
113
132
|
const llmInput = {
|
|
114
133
|
siteSchemaTsDefs: getSiteSchemaTsDefs(),
|
|
115
134
|
predefinedFields: JSON.stringify(getPredefinedFields(), undefined, 2),
|
|
116
135
|
siteSchemaJson: JSON.stringify(siteSchema, undefined, 2),
|
|
117
136
|
message: input.prompt,
|
|
118
137
|
};
|
|
119
|
-
const debug = await debugLlmOutput(ctx,
|
|
138
|
+
const debug = await debugLlmOutput(ctx, llmTaskName, ctx.anthropicModelName, stepHandle, {
|
|
120
139
|
message: llmInput.message,
|
|
121
140
|
siteSchemaJson: llmInput.siteSchemaJson,
|
|
122
141
|
});
|
|
123
|
-
let
|
|
124
|
-
if (!
|
|
125
|
-
const
|
|
126
|
-
|
|
142
|
+
let llmOutput = debug.stored;
|
|
143
|
+
if (!llmOutput) {
|
|
144
|
+
const prompt = fieldsPrompt(llmInput);
|
|
145
|
+
const { messageContent, report } = await invokeClaude(ctx, {
|
|
146
|
+
llmTaskName,
|
|
147
|
+
prompt,
|
|
148
|
+
maxTokens: 700,
|
|
149
|
+
temperature: 0.1,
|
|
150
|
+
systemInstruction: "beFast",
|
|
151
|
+
});
|
|
152
|
+
llmOutput = await debug.getMessageContent(messageContent, report);
|
|
127
153
|
}
|
|
128
|
-
const { assignedFields, unusedInformation } = parseLlmResponseAsProperties(
|
|
154
|
+
const { assignedFields, unusedInformation } = parseLlmResponseAsProperties(llmOutput.output, [
|
|
129
155
|
{
|
|
130
156
|
tagName: "yaml_result",
|
|
131
157
|
key: "assignedFields",
|
|
@@ -141,7 +167,7 @@ siteSchema) {
|
|
|
141
167
|
if (siteSchema.nodeTypes) {
|
|
142
168
|
assignFieldsToNodeTypes(ctx, assignedFields, siteSchema.nodeTypes);
|
|
143
169
|
}
|
|
144
|
-
return { unusedInformation };
|
|
170
|
+
return { unusedInformation, llmReport: llmOutput.llmReport };
|
|
145
171
|
}
|
|
146
172
|
function assignFieldsToNodeTypes(ctx, assignedFields, nodeTypes) {
|
|
147
173
|
const remainingTypeNames = new Set(Object.keys(assignedFields));
|
|
@@ -1,56 +1,92 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadStep, readStepSchema } from "../../db/db-read.queries.js";
|
|
2
|
+
import { insertStep, saveCompletedSchemaStep, updateStep, } from "../../db/db-write.queries.js";
|
|
3
|
+
import { invokeClaude } from "../lib/calling-llm-anthropic.js";
|
|
2
4
|
import { createPromptTemplate, getPredefinedFields, getSiteSchemaTsDefs, } from "../lib/create-prompt.js";
|
|
3
5
|
import { debugLlmOutput } from "../lib/debug-utils.js";
|
|
4
6
|
import { parseLlmResponseAsProperties } from "../lib/parse-llm-response.js";
|
|
7
|
+
import { safeCallStep } from "../lib/session-utils.js";
|
|
5
8
|
const prompt1Tpl = await createPromptTemplate({
|
|
6
9
|
fileName: "update-site-schema-1-write-details.md",
|
|
7
10
|
});
|
|
8
11
|
const prompt2Tpl = await createPromptTemplate({
|
|
9
12
|
fileName: "update-site-schema-2-execute.md",
|
|
10
13
|
});
|
|
11
|
-
export async function
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
changed: false,
|
|
18
|
-
explanation: task.explanation,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
const genSchema = await invokeUpdateSiteSchemaStep2(ctx, {
|
|
22
|
-
taskDetailsMd: task.taskDetailsMd,
|
|
23
|
-
generatedSchema: input.generatedSchema,
|
|
14
|
+
export async function startUpdateSiteSchema(ctx, input) {
|
|
15
|
+
const fromStepSchema = await readStepSchema(ctx, input.fromStepNumber);
|
|
16
|
+
const stepHandle = await insertStep(ctx, {
|
|
17
|
+
kind: "updateSchema",
|
|
18
|
+
status: "pending",
|
|
19
|
+
currentActivity: "updating1",
|
|
24
20
|
});
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
safeCallStep(ctx, stepHandle, () => invokeUpdateSiteSchema(ctx, stepHandle, { prompt: input.prompt, fromStepSchema }));
|
|
22
|
+
return await loadStep(ctx, stepHandle.stepNumber);
|
|
23
|
+
}
|
|
24
|
+
export async function invokeUpdateSiteSchema(ctx, stepHandle, input, { asRemainingOf } = {}) {
|
|
25
|
+
const { properties, llmReport: llmReport1 } = await invokeUpdateSiteSchemaStep1(ctx, input, stepHandle);
|
|
26
|
+
if (!properties.taskDetailsMd) {
|
|
27
|
+
await updateStep(ctx, stepHandle, {
|
|
28
|
+
status: "noEffect",
|
|
29
|
+
currentActivity: null,
|
|
30
|
+
explanation: properties.explanation,
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
await updateStep(ctx, stepHandle, {
|
|
35
|
+
currentActivity: "updating2",
|
|
36
|
+
explanation: properties.explanation,
|
|
37
|
+
});
|
|
38
|
+
const { stepSchema, llmReport: llmReport2 } = await invokeUpdateSiteSchemaStep2(ctx, {
|
|
39
|
+
taskDetailsMd: properties.taskDetailsMd,
|
|
40
|
+
fromStepSchema: input.fromStepSchema,
|
|
41
|
+
}, stepHandle);
|
|
42
|
+
const completedValues = {
|
|
43
|
+
...stepSchema,
|
|
44
|
+
status: "completed",
|
|
45
|
+
inputTokenCount: llmReport1.inputTokenCount + llmReport2.inputTokenCount,
|
|
46
|
+
outputTokenCount: (llmReport1.outputTokenCount ?? 0) + (llmReport2.outputTokenCount ?? 0),
|
|
47
|
+
promptTitle: undefined, // TODO: implement prompt title
|
|
32
48
|
};
|
|
49
|
+
if (asRemainingOf) {
|
|
50
|
+
completedValues.inputTokenCount += asRemainingOf.inputTokenCount;
|
|
51
|
+
completedValues.outputTokenCount += asRemainingOf.outputTokenCount;
|
|
52
|
+
completedValues.promptTitle = undefined;
|
|
53
|
+
if (asRemainingOf.explanation) {
|
|
54
|
+
completedValues.explanation = properties.explanation
|
|
55
|
+
? `${asRemainingOf.explanation}\n\n${properties.explanation}`
|
|
56
|
+
: asRemainingOf.explanation;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await saveCompletedSchemaStep(ctx, stepHandle, completedValues);
|
|
33
60
|
}
|
|
34
|
-
async function invokeUpdateSiteSchemaStep1(ctx, input) {
|
|
35
|
-
const
|
|
61
|
+
async function invokeUpdateSiteSchemaStep1(ctx, input, stepHandle) {
|
|
62
|
+
const llmTaskName = "update-step1";
|
|
36
63
|
const llmInput = {
|
|
37
64
|
siteSchemaTsDefs: getSiteSchemaTsDefs(),
|
|
38
65
|
predefinedFields: JSON.stringify(getPredefinedFields(), undefined, 2),
|
|
39
|
-
siteSchemaJson: JSON.stringify(input.
|
|
40
|
-
l10nJson: JSON.stringify(input.
|
|
66
|
+
siteSchemaJson: JSON.stringify(input.fromStepSchema.siteSchema, undefined, 2),
|
|
67
|
+
l10nJson: JSON.stringify(input.fromStepSchema.l10n, undefined, 2),
|
|
41
68
|
updateMessage: input.prompt,
|
|
42
69
|
};
|
|
43
|
-
const debug = await debugLlmOutput(ctx,
|
|
70
|
+
const debug = await debugLlmOutput(ctx, llmTaskName, ctx.anthropicModelName, stepHandle, {
|
|
44
71
|
updateMessage: llmInput.updateMessage,
|
|
45
72
|
siteSchemaJson: llmInput.siteSchemaJson,
|
|
46
73
|
l10nJson: llmInput.l10nJson,
|
|
47
74
|
});
|
|
48
|
-
let
|
|
49
|
-
if (!
|
|
50
|
-
|
|
51
|
-
|
|
75
|
+
let llmOutput = debug.stored;
|
|
76
|
+
if (!llmOutput) {
|
|
77
|
+
// Create formatted messages using the template
|
|
78
|
+
const message = prompt1Tpl(llmInput);
|
|
79
|
+
// Call the model
|
|
80
|
+
const { messageContent, report } = await invokeClaude(ctx, {
|
|
81
|
+
llmTaskName,
|
|
82
|
+
prompt: message,
|
|
83
|
+
maxTokens: 1_500,
|
|
84
|
+
temperature: 0.1,
|
|
85
|
+
systemInstruction: "beSmart",
|
|
86
|
+
});
|
|
87
|
+
llmOutput = await debug.getMessageContent(messageContent, report);
|
|
52
88
|
}
|
|
53
|
-
|
|
89
|
+
const properties = parseLlmResponseAsProperties(llmOutput.output, [
|
|
54
90
|
{
|
|
55
91
|
tagName: "task_details_md",
|
|
56
92
|
key: "taskDetailsMd",
|
|
@@ -63,27 +99,40 @@ async function invokeUpdateSiteSchemaStep1(ctx, input) {
|
|
|
63
99
|
format: "markdown",
|
|
64
100
|
},
|
|
65
101
|
]);
|
|
102
|
+
return {
|
|
103
|
+
properties,
|
|
104
|
+
llmReport: llmOutput.llmReport,
|
|
105
|
+
};
|
|
66
106
|
}
|
|
67
|
-
async function invokeUpdateSiteSchemaStep2(ctx, input) {
|
|
68
|
-
const
|
|
107
|
+
async function invokeUpdateSiteSchemaStep2(ctx, input, stepHandle) {
|
|
108
|
+
const llmTaskName = "update-step2";
|
|
69
109
|
const llmInput = {
|
|
70
110
|
siteSchemaTsDefs: getSiteSchemaTsDefs(),
|
|
71
111
|
predefinedFields: JSON.stringify(getPredefinedFields(), undefined, 2),
|
|
72
|
-
siteSchemaJson: JSON.stringify(input.
|
|
73
|
-
l10nJson: JSON.stringify(input.
|
|
112
|
+
siteSchemaJson: JSON.stringify(input.fromStepSchema.siteSchema, undefined, 2),
|
|
113
|
+
l10nJson: JSON.stringify(input.fromStepSchema.l10n, undefined, 2),
|
|
74
114
|
taskDetailsMd: input.taskDetailsMd,
|
|
75
115
|
};
|
|
76
|
-
const debug = await debugLlmOutput(ctx,
|
|
116
|
+
const debug = await debugLlmOutput(ctx, llmTaskName, ctx.anthropicModelName, stepHandle, {
|
|
77
117
|
taskDetailsMd: llmInput.taskDetailsMd,
|
|
78
118
|
siteSchemaJson: llmInput.siteSchemaJson,
|
|
79
119
|
l10nJson: llmInput.l10nJson,
|
|
80
120
|
});
|
|
81
|
-
let
|
|
82
|
-
if (!
|
|
83
|
-
|
|
84
|
-
|
|
121
|
+
let llmOutput = debug.stored;
|
|
122
|
+
if (!llmOutput) {
|
|
123
|
+
// Create formatted messages using the template
|
|
124
|
+
const message = prompt2Tpl(llmInput);
|
|
125
|
+
// Call the model
|
|
126
|
+
const { messageContent, report } = await invokeClaude(ctx, {
|
|
127
|
+
llmTaskName,
|
|
128
|
+
prompt: message,
|
|
129
|
+
maxTokens: 7_000,
|
|
130
|
+
temperature: 0.1,
|
|
131
|
+
systemInstruction: "beSmart",
|
|
132
|
+
});
|
|
133
|
+
llmOutput = await debug.getMessageContent(messageContent, report);
|
|
85
134
|
}
|
|
86
|
-
const parsed = parseLlmResponseAsProperties(
|
|
135
|
+
const parsed = parseLlmResponseAsProperties(llmOutput.output, [
|
|
87
136
|
{
|
|
88
137
|
tagName: "updated_site_schema_json",
|
|
89
138
|
key: "siteSchema",
|
|
@@ -98,7 +147,7 @@ async function invokeUpdateSiteSchemaStep2(ctx, input) {
|
|
|
98
147
|
},
|
|
99
148
|
]);
|
|
100
149
|
const result = {
|
|
101
|
-
...input.
|
|
150
|
+
...input.fromStepSchema,
|
|
102
151
|
};
|
|
103
152
|
if (parsed.siteSchema) {
|
|
104
153
|
result.siteSchema = fixSiteSchema(parsed.siteSchema);
|
|
@@ -106,16 +155,30 @@ async function invokeUpdateSiteSchemaStep2(ctx, input) {
|
|
|
106
155
|
if (parsed.l10n) {
|
|
107
156
|
result.l10n = parsed.l10n;
|
|
108
157
|
}
|
|
109
|
-
return
|
|
158
|
+
return {
|
|
159
|
+
stepSchema: result,
|
|
160
|
+
llmReport: llmOutput.llmReport,
|
|
161
|
+
};
|
|
110
162
|
}
|
|
111
163
|
function fixSiteSchema(siteSchema) {
|
|
112
164
|
for (const nodeType of siteSchema.nodeTypes ?? []) {
|
|
113
|
-
|
|
165
|
+
if (!nodeType.fields)
|
|
166
|
+
continue;
|
|
167
|
+
// Add Quill plugin to fields with renderAs "html"
|
|
168
|
+
for (const field of nodeType.fields) {
|
|
114
169
|
if (typeof field !== "string" && field.renderAs === "html") {
|
|
115
170
|
field.dataType = "json";
|
|
116
171
|
field.plugin = "@paroicms/quill-editor-plugin";
|
|
117
172
|
}
|
|
118
173
|
}
|
|
174
|
+
// Move labeling fields to the beginning of the fields array
|
|
175
|
+
const labelingFields = nodeType.fields.filter((f) => typeof f !== "string" && f.storedAs === "labeling");
|
|
176
|
+
if (labelingFields.length > 0) {
|
|
177
|
+
nodeType.fields = [
|
|
178
|
+
...labelingFields,
|
|
179
|
+
...nodeType.fields.filter((f) => typeof f === "string" || f.storedAs !== "labeling"),
|
|
180
|
+
];
|
|
181
|
+
}
|
|
119
182
|
}
|
|
120
183
|
return siteSchema;
|
|
121
184
|
}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { generateSlug } from "@paroicms/public-anywhere-lib";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
+
import { loadStep, readStepSchema } from "../../db/db-read.queries.js";
|
|
6
|
+
import { insertStep, saveGeneratedSiteStep, } from "../../db/db-write.queries.js";
|
|
5
7
|
import { fillSiteWithFakeContent } from "../fake-content-generator.ts/create-database-with-fake-content.js";
|
|
8
|
+
import { safeCallStep } from "../lib/session-utils.js";
|
|
6
9
|
import { createTheme } from "./theme-creator.js";
|
|
7
10
|
export async function generateSite(ctx, input) {
|
|
8
11
|
const { service, logger, sitesDir, packConf: { packName }, } = ctx;
|
|
9
|
-
const {
|
|
10
|
-
const
|
|
12
|
+
const { fromStepNumber, withSampleData } = input;
|
|
13
|
+
const stepHandle = await insertStep(ctx, {
|
|
14
|
+
kind: "generateSite",
|
|
15
|
+
status: "pending",
|
|
16
|
+
currentActivity: "generatingSite",
|
|
17
|
+
});
|
|
18
|
+
const { l10n, siteSchema, localizedValues } = await readStepSchema(ctx, fromStepNumber);
|
|
19
|
+
const siteId = randomUUID();
|
|
11
20
|
logger.info(`Generating site: ${siteId}…`);
|
|
12
21
|
const siteDir = join(sitesDir, siteId);
|
|
13
22
|
await mkdir(siteDir);
|
|
@@ -16,7 +25,9 @@ export async function generateSite(ctx, input) {
|
|
|
16
25
|
await writeFile(join(siteDir, `site-schema.l10n.${language}.json`), JSON.stringify(l10nData, null, 2), "utf-8");
|
|
17
26
|
}
|
|
18
27
|
await writeFile(join(siteDir, "package.json"), JSON.stringify(getPackageJsonContent({
|
|
19
|
-
siteTitle: siteTitle.en ??
|
|
28
|
+
siteTitle: localizedValues.siteTitle.en ??
|
|
29
|
+
Object.values(localizedValues.siteTitle)[0] ??
|
|
30
|
+
"new-website",
|
|
20
31
|
}), null, 2), "utf-8");
|
|
21
32
|
await createTheme(ctx, siteDir, siteSchema);
|
|
22
33
|
const regSite = await service.connector.registerNewSite({
|
|
@@ -25,12 +36,6 @@ export async function generateSite(ctx, input) {
|
|
|
25
36
|
domain: siteId,
|
|
26
37
|
version: "0.0.0",
|
|
27
38
|
});
|
|
28
|
-
if (withFakeContent) {
|
|
29
|
-
const report = await fillSiteWithFakeContent(ctx, { regSite, siteTitle });
|
|
30
|
-
await updateSession(ctx, {
|
|
31
|
-
contentCountInc: report.getContentCount(),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
39
|
const account = {
|
|
35
40
|
kind: "local",
|
|
36
41
|
email: `${siteId}@yopmail.com`,
|
|
@@ -39,16 +44,19 @@ export async function generateSite(ctx, input) {
|
|
|
39
44
|
};
|
|
40
45
|
await ctx.service.connector.createAccount(regSite.fqdn, account, { asContactEmail: true });
|
|
41
46
|
const { siteUrl } = regSite;
|
|
42
|
-
|
|
43
|
-
status: "
|
|
44
|
-
nodeTypeCount: siteSchema.nodeTypes?.length ?? 0,
|
|
45
|
-
});
|
|
46
|
-
return {
|
|
47
|
+
const values = {
|
|
48
|
+
status: withSampleData ? "pending" : "completed",
|
|
47
49
|
siteId,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
account,
|
|
50
|
+
siteUrl,
|
|
51
|
+
localizedValues,
|
|
52
|
+
loginEmail: account.email,
|
|
53
|
+
loginPassword: account.password,
|
|
51
54
|
};
|
|
55
|
+
await saveGeneratedSiteStep(ctx, stepHandle, values);
|
|
56
|
+
if (withSampleData) {
|
|
57
|
+
safeCallStep(ctx, stepHandle, () => fillSiteWithFakeContent(ctx, stepHandle, { regSite, localizedValues }));
|
|
58
|
+
}
|
|
59
|
+
return await loadStep(ctx, stepHandle.stepNumber);
|
|
52
60
|
}
|
|
53
61
|
function getPackageJsonContent(options) {
|
|
54
62
|
return {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { Mistral } from "@mistralai/mistralai";
|
|
3
|
+
import { getJwtSecretSync } from "@paroicms/internal-server-lib";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
export function createRawContext(service, options) {
|
|
6
|
+
const { cn, logNextQuery, pluginConf, debugDir } = options;
|
|
7
|
+
const packConf = service.connector.getSitePackConf(pluginConf.packName);
|
|
8
|
+
const { sitesDir, packName } = packConf;
|
|
9
|
+
if (!sitesDir || packConf.serveOn !== "subDomain") {
|
|
10
|
+
throw new Error(`Site-generator plugin can generate sites only for sub-domain pack with "sitesDir", but pack "${packName}" doesn't have it`);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
cn,
|
|
14
|
+
logNextQuery,
|
|
15
|
+
jwtSecret: getJwtSecretSync(join(service.registeredSite.dataDir, "site-generator-secret.txt")),
|
|
16
|
+
pluginConf,
|
|
17
|
+
debugDir,
|
|
18
|
+
sitesDir,
|
|
19
|
+
packConf,
|
|
20
|
+
service,
|
|
21
|
+
logger: service.logger,
|
|
22
|
+
anthropic: new Anthropic({
|
|
23
|
+
apiKey: pluginConf.anthropicApiKey,
|
|
24
|
+
}),
|
|
25
|
+
mistral: new Mistral({
|
|
26
|
+
apiKey: pluginConf.mistralApiKey,
|
|
27
|
+
}),
|
|
28
|
+
mistralModelName: "ministral-8b-2410",
|
|
29
|
+
anthropicModelName: "claude-3-7-sonnet-20250219",
|
|
30
|
+
};
|
|
31
|
+
}
|