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}` };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user