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:
95
packages/estimation-engine/src/steps/05-synthesize.ts
Normal file
95
packages/estimation-engine/src/steps/05-synthesize.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// ============================================================================
|
||||
// Step 05: Synthesize — Position Descriptions (Gemini Pro)
|
||||
// ============================================================================
|
||||
|
||||
import { llmJsonRequest } from "../llm-client.js";
|
||||
import type { EstimationState, StepResult, PipelineConfig } from "../types.js";
|
||||
import { DEFAULT_MODELS } from "../types.js";
|
||||
|
||||
export async function executeSynthesize(
|
||||
state: EstimationState,
|
||||
config: PipelineConfig,
|
||||
): Promise<StepResult> {
|
||||
const models = { ...DEFAULT_MODELS, ...config.modelsOverride };
|
||||
const startTime = Date.now();
|
||||
|
||||
if (!state.concept?.auditedFacts || !state.concept?.architecture?.sitemap) {
|
||||
return { success: false, error: "Missing audited facts or sitemap." };
|
||||
}
|
||||
|
||||
const facts = state.concept.auditedFacts;
|
||||
|
||||
// Determine which positions are required
|
||||
const requiredPositions = [
|
||||
"Das technische Fundament",
|
||||
(facts.selectedPages?.length || 0) + (facts.otherPages?.length || 0) > 0
|
||||
? "Individuelle Seiten"
|
||||
: null,
|
||||
facts.features?.length > 0 ? "System-Module (Features)" : null,
|
||||
facts.functions?.length > 0 ? "Logik-Funktionen" : null,
|
||||
facts.apiSystems?.length > 0 ? "Schnittstellen (API)" : null,
|
||||
facts.cmsSetup ? "Inhalts-Verwaltung" : null,
|
||||
facts.multilang ? "Mehrsprachigkeit" : null,
|
||||
"Inhaltliche Initial-Pflege",
|
||||
"Sorglos Betrieb",
|
||||
].filter(Boolean);
|
||||
|
||||
const systemPrompt = `
|
||||
You are a Senior Solution Architect. Write position descriptions for a professional B2B quote.
|
||||
|
||||
### REQUIRED POSITIONS (STRICT — ONLY DESCRIBE THESE):
|
||||
${requiredPositions.map((p) => `"${p}"`).join(", ")}
|
||||
|
||||
### RULES (STRICT):
|
||||
1. NO FIRST PERSON: NEVER "Ich", "Mein", "Wir", "Unser". Lead with nouns or passive verbs.
|
||||
2. QUANTITY PARITY: Description MUST list EXACTLY the number of items matching 'qty'.
|
||||
3. CMS GUARD: If cmsSetup=false, do NOT mention "CMS", "Inhaltsverwaltung". Use "Plattform-Struktur".
|
||||
4. TONE: "Erstellung von...", "Anbindung der...", "Bereitstellung von...". Technical, high-density.
|
||||
5. PAGES: List actual page names. NO implementation notes in parentheses.
|
||||
6. HARD SPECIFICS: Use industry terms from the briefing (e.g. "Kabeltiefbau", "110 kV").
|
||||
7. KEYS: Return EXACTLY the keys from REQUIRED POSITIONS.
|
||||
8. NO AGB: NEVER mention "AGB" or "Geschäftsbedingungen".
|
||||
9. Sorglos Betrieb: "Inklusive 1 Jahr technischer Betrieb, Hosting, SSL, Sicherheits-Updates, Monitoring und techn. Support."
|
||||
10. Inhaltliche Initial-Pflege: Refers to DATENSÄTZE (datasets like products, references), NOT Seiten.
|
||||
Use "Datensätze" in the description, not "Seiten".
|
||||
11. Mehrsprachigkeit: This is a +20% markup on the subtotal. NOT an API. NOT a Schnittstelle.
|
||||
|
||||
### EXAMPLES:
|
||||
- GOOD: "Erstellung der Seiten: Startseite, Über uns, Leistungen, Kontakt."
|
||||
- GOOD: "Native API-Anbindung an Google Maps mit individueller Standort-Visualisierung."
|
||||
- BAD: "Ich richte dir das CMS ein."
|
||||
- BAD: "Verschiedene Funktionen" (too generic — name the things!)
|
||||
|
||||
### DATA CONTEXT:
|
||||
${JSON.stringify({ facts, sitemap: state.concept.architecture.sitemap, strategy: { briefingSummary: state.concept.strategy.briefingSummary } }, null, 2)}
|
||||
|
||||
### OUTPUT FORMAT:
|
||||
{
|
||||
"positionDescriptions": { "Das technische Fundament": string, ... }
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
const { data, usage } = await llmJsonRequest({
|
||||
model: models.pro,
|
||||
systemPrompt,
|
||||
userPrompt: state.concept.briefing,
|
||||
apiKey: config.openrouterKey,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: data.positionDescriptions || data,
|
||||
usage: {
|
||||
step: "05-synthesize",
|
||||
model: models.pro,
|
||||
promptTokens: usage.promptTokens,
|
||||
completionTokens: usage.completionTokens,
|
||||
cost: usage.cost,
|
||||
durationMs: Date.now() - startTime,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
return { success: false, error: `Synthesize step failed: ${(err as Error).message}` };
|
||||
}
|
||||
}
|
||||
99
packages/estimation-engine/src/steps/06-critique.ts
Normal file
99
packages/estimation-engine/src/steps/06-critique.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
// ============================================================================
|
||||
// Step 06: Critique — Industrial Critic Quality Gate (Claude Opus)
|
||||
// ============================================================================
|
||||
|
||||
import { llmJsonRequest } from "../llm-client.js";
|
||||
import type { EstimationState, StepResult, PipelineConfig } from "../types.js";
|
||||
import { DEFAULT_MODELS } from "../types.js";
|
||||
|
||||
export async function executeCritique(
|
||||
state: EstimationState,
|
||||
config: PipelineConfig,
|
||||
): Promise<StepResult> {
|
||||
const models = { ...DEFAULT_MODELS, ...config.modelsOverride };
|
||||
const startTime = Date.now();
|
||||
|
||||
const currentState = {
|
||||
facts: state.concept?.auditedFacts,
|
||||
briefingSummary: state.concept?.strategy?.briefingSummary,
|
||||
designVision: state.concept?.strategy?.designVision,
|
||||
sitemap: state.concept?.architecture?.sitemap,
|
||||
positionDescriptions: state.positionDescriptions,
|
||||
siteProfile: state.concept?.siteProfile
|
||||
? {
|
||||
existingFeatures: state.concept.siteProfile.existingFeatures,
|
||||
services: state.concept.siteProfile.services,
|
||||
externalDomains: state.concept.siteProfile.externalDomains,
|
||||
navigation: state.concept.siteProfile.navigation,
|
||||
totalPages: state.concept.siteProfile.totalPages,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
|
||||
const systemPrompt = `
|
||||
You are the "Industrial Critic" — the final quality gate for a professional B2B estimation.
|
||||
Your job is to find EVERY error, hallucination, and inconsistency before this goes to the client.
|
||||
|
||||
### CRITICAL ERROR CHECKLIST (FAIL IF ANY FOUND):
|
||||
1. HALLUCINATION: FAIL if names, software versions, or details not in the BRIEFING are used.
|
||||
- "Sie", "Ansprechpartner" for personName when an actual name exists = FAIL.
|
||||
2. LOGIC CONFLICT: FAIL if isRelaunch=true but text claims "no website exists".
|
||||
3. IMPLEMENTATION FLUFF: FAIL if "React", "Next.js", "TypeScript", "Tailwind" are mentioned.
|
||||
4. GENERICISM: FAIL if text could apply to ANY company. Must use specific industry terms.
|
||||
5. NAMEN-VERBOT: FAIL if personal names in briefingSummary or designVision.
|
||||
6. CMS-LEAKAGE: FAIL if cmsSetup=false but descriptions mention "CMS", "Inhaltsverwaltung".
|
||||
7. AGB BAN: FAIL if "AGB" or "Geschäftsbedingungen" appear anywhere.
|
||||
8. LENGTH: briefingSummary ~6 sentences, designVision ~4 sentences. Shorten if too wordy.
|
||||
9. LEGAL SAFETY: FAIL if "rechtssicher" is used. Use "Standard-konform" instead.
|
||||
10. BULLSHIT DETECTOR: FAIL if jargon like "SEO-Standards zur Fachkräftesicherung",
|
||||
"B2B-Nutzerströme", "Digitale Konvergenzstrategie" or similar meaningless buzzwords are used.
|
||||
The text must make SENSE to a construction industry CEO.
|
||||
11. PAGE STRUCTURE: FAIL if the sitemap contains:
|
||||
- Videos as pages (Messefilm, Imagefilm)
|
||||
- Internal functions as pages (Verwaltung)
|
||||
- Entities with their own domains as sub-pages (check externalDomains!)
|
||||
12. SORGLOS-BETRIEB: FAIL if not mentioned in the summary or position descriptions.
|
||||
13. TONE: FAIL if "wir/unser" or "Ich/Mein" in position descriptions. FAIL if marketing fluff.
|
||||
14. MULTILANG: FAIL if Mehrsprachigkeit is described as an API or Schnittstelle.
|
||||
15. INITIAL-PFLEGE: FAIL if described in terms of "Seiten" instead of "Datensätze".
|
||||
|
||||
### MISSION:
|
||||
Return corrected fields ONLY for fields with issues. If everything passes, return empty corrections.
|
||||
|
||||
### OUTPUT FORMAT:
|
||||
{
|
||||
"passed": boolean,
|
||||
"errors": [{ "field": string, "issue": string, "severity": "critical" | "warning" }],
|
||||
"corrections": {
|
||||
"briefingSummary"?: string,
|
||||
"designVision"?: string,
|
||||
"positionDescriptions"?: Record<string, string>,
|
||||
"sitemap"?: array
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
const { data, usage } = await llmJsonRequest({
|
||||
model: models.opus,
|
||||
systemPrompt,
|
||||
userPrompt: `BRIEFING_TRUTH:\n${state.concept?.briefing}\n\nCURRENT_STATE:\n${JSON.stringify(currentState, null, 2)}`,
|
||||
apiKey: config.openrouterKey,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
usage: {
|
||||
step: "06-critique",
|
||||
model: models.opus,
|
||||
promptTokens: usage.promptTokens,
|
||||
completionTokens: usage.completionTokens,
|
||||
cost: usage.cost,
|
||||
durationMs: Date.now() - startTime,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
return { success: false, error: `Critique step failed: ${(err as Error).message}` };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user