feat: migrate npm registry from Verdaccio to Gitea Packages
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Failing after 35s
Monorepo Pipeline / 🧪 Test (push) Failing after 35s
Monorepo Pipeline / 🏗️ Build (push) Failing after 12s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Image Processor (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Failing after 35s
Monorepo Pipeline / 🧪 Test (push) Failing after 35s
Monorepo Pipeline / 🏗️ Build (push) Failing after 12s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Image Processor (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
This commit is contained in:
133
packages/concept-engine/src/steps/04-architect.ts
Normal file
133
packages/concept-engine/src/steps/04-architect.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
// ============================================================================
|
||||
// Step 04: Architect — Sitemap & Information Architecture (Gemini Pro)
|
||||
// ============================================================================
|
||||
|
||||
import { llmJsonRequest } from "../llm-client.js";
|
||||
import type { ConceptState, StepResult, PipelineConfig } from "../types.js";
|
||||
import { DEFAULT_MODELS } from "../types.js";
|
||||
|
||||
export async function executeArchitect(
|
||||
state: ConceptState,
|
||||
config: PipelineConfig,
|
||||
): Promise<StepResult> {
|
||||
const models = { ...DEFAULT_MODELS, ...config.modelsOverride };
|
||||
const startTime = Date.now();
|
||||
|
||||
if (!state.auditedFacts) {
|
||||
return { success: false, error: "No audited facts available." };
|
||||
}
|
||||
|
||||
// Build navigation constraint from the real site
|
||||
const existingNav = state.siteProfile?.navigation?.map((n) => n.label).join(", ") || "unbekannt";
|
||||
const existingServices = state.siteProfile?.services?.join(", ") || "unbekannt";
|
||||
const externalDomains = state.siteProfile?.externalDomains?.join(", ") || "keine";
|
||||
|
||||
const systemPrompt = `
|
||||
Du bist ein Senior UX Architekt. Erstelle einen ECHTEN SEITENBAUM für die neue Website.
|
||||
Regelwerk für den Output:
|
||||
|
||||
### SEITENBAUM-REGELN:
|
||||
1. KEIN MARKETINGSPRECH als Kategoriename. Gültige Kategorien sind nur die echten Navigationspunkte der Website.
|
||||
ERLAUBT: "Startseite", "Leistungen", "Über uns", "Karriere", "Referenzen", "Kontakt", "Rechtliches"
|
||||
VERBOTEN: "Kern-Präsenz", "Vertrauen", "Business Areas", "Digitaler Auftritt"
|
||||
|
||||
2. LEISTUNGEN muss in ECHTE UNTERSEITEN aufgeteilt werden — nicht eine einzige "Leistungen"-Seite.
|
||||
Jede Kompetenz aus dem existierenden Leistungsspektrum = eine eigene Seite.
|
||||
Beispiel statt:
|
||||
{ category: "Leistungen", pages: [{ title: "Leistungen", desc: "..." }] }
|
||||
So:
|
||||
{ category: "Leistungen", pages: [
|
||||
{ title: "Kabeltiefbau", desc: "Mittelspannung, Niederspannung, Kabelpflugarbeiten..." },
|
||||
{ title: "Horizontalspülbohrungen", desc: "HDD in allen Bodenklassen..." },
|
||||
{ title: "Elektromontagen", desc: "Bis 110 kV, Glasfaserkabelmontagen..." },
|
||||
{ title: "Planung & Dokumentation", desc: "Genehmigungs- und Ausführungsplanung, Vermessung..." }
|
||||
]}
|
||||
|
||||
3. SEITENTITEL: Kurz, klar, faktisch. Kein Werbejargon.
|
||||
ERLAUBT: "Kabeltiefbau", "Über uns", "Karriere"
|
||||
VERBOTEN: "Unsere Expertise", "Kompetenzspektrum", "Community"
|
||||
|
||||
4. Gruppe die Leistungen nach dem ECHTEN Kompetenzkatalog der bestehenden Site — nicht erfinden.
|
||||
|
||||
5. Keine doppelten Seiten. Keine Phantomseiten.
|
||||
|
||||
6. Videos = Content-Assets, keine eigene Seite.
|
||||
|
||||
7. Entitäten mit eigener Domain (${externalDomains}) = NICHT als Seite. Nur als Teaser/Link wenn nötig.
|
||||
|
||||
### KONTEXT:
|
||||
Bestehende Navigation: ${existingNav}
|
||||
Bestehende Services: ${existingServices}
|
||||
Externe Domains (haben eigene Website): ${externalDomains}
|
||||
Angeforderte zusätzliche Seiten aus Briefing: ${(state.auditedFacts as any)?.pages?.join(", ") || "keine spezifischen"}
|
||||
|
||||
### OUTPUT FORMAT (JSON):
|
||||
{
|
||||
"websiteTopic": string, // MAX 3 Wörter, beschreibend
|
||||
"sitemap": [
|
||||
{
|
||||
"category": string, // Echter Nav-Eintrag. KEIN Marketingsprech.
|
||||
"pages": [
|
||||
{ "title": string, "desc": string } // Echte Unterseite, 1-2 Sätze Zweck
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
const userPrompt = `
|
||||
BRIEFING:
|
||||
${state.briefing}
|
||||
|
||||
FAKTEN (aus Extraktion):
|
||||
${JSON.stringify({ facts: state.auditedFacts, strategy: { briefingSummary: state.briefingSummary } }, null, 2)}
|
||||
|
||||
Erstelle den Seitenbaum. Baue die Leistungen DETAILLIERT aus — echte Unterseiten pro Kompetenzbereich.
|
||||
`;
|
||||
|
||||
try {
|
||||
const { data, usage } = await llmJsonRequest({
|
||||
model: models.pro,
|
||||
systemPrompt,
|
||||
userPrompt,
|
||||
apiKey: config.openrouterKey,
|
||||
});
|
||||
|
||||
// Normalize sitemap structure
|
||||
let sitemap = data.sitemap;
|
||||
if (sitemap && !Array.isArray(sitemap)) {
|
||||
if (sitemap.categories) sitemap = sitemap.categories;
|
||||
else {
|
||||
const entries = Object.entries(sitemap);
|
||||
if (entries.every(([, v]) => Array.isArray(v))) {
|
||||
sitemap = entries.map(([category, pages]) => ({ category, pages }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(sitemap)) {
|
||||
sitemap = sitemap.map((cat: any) => ({
|
||||
category: cat.category || cat.kategorie || cat.Kategorie || "Allgemein",
|
||||
pages: (cat.pages || cat.seiten || []).map((page: any) => ({
|
||||
title: page.title || page.titel || "Seite",
|
||||
desc: page.desc || page.beschreibung || page.description || "",
|
||||
})),
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: { websiteTopic: data.websiteTopic, sitemap },
|
||||
usage: {
|
||||
step: "04-architect",
|
||||
model: models.pro,
|
||||
promptTokens: usage.promptTokens,
|
||||
completionTokens: usage.completionTokens,
|
||||
cost: usage.cost,
|
||||
durationMs: Date.now() - startTime,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
return { success: false, error: `Architect step failed: ${(err as Error).message}` };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user