From 757ab4f842354c72cfd9373005967df19a722906 Mon Sep 17 00:00:00 2001 From: Jackson Chen <541898146chen@gmail.com> Date: Mon, 6 Jan 2025 22:38:34 -0500 Subject: [PATCH 1/2] feat(backend): update file structure handling and introduce Level2UXSitemapStructureHandler --- .../__tests__/test-database-schemas.spec.ts | 49 ------- .../__tests__/test-generate-doc.spec.ts | 80 +++-------- .../__tests__/test.fullstack-gen.spec.ts | 136 ++---------------- .../__tests__/test.model-provider.spec.ts | 17 ++- backend/src/build-system/__tests__/utils.ts | 134 +++++++++++++++++ .../handlers/file-manager/file-arch/index.ts | 2 +- .../file-manager/file-structure/index.ts | 16 ++- .../handlers/ux/sitemap-structure/index.ts | 89 ------------ .../handlers/ux/sitemap-structure/sms-page.ts | 96 +++++++++++++ backend/src/build-system/hanlder-manager.ts | 2 + backend/src/build-system/types.ts | 15 +- 11 files changed, 300 insertions(+), 336 deletions(-) delete mode 100644 backend/src/build-system/__tests__/test-database-schemas.spec.ts create mode 100644 backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts diff --git a/backend/src/build-system/__tests__/test-database-schemas.spec.ts b/backend/src/build-system/__tests__/test-database-schemas.spec.ts deleted file mode 100644 index b95108f8..00000000 --- a/backend/src/build-system/__tests__/test-database-schemas.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { BuilderContext } from 'src/build-system/context'; -import { DBSchemaHandler } from '../handlers/database/schemas/schemas'; -import { readFileSync } from 'fs'; -import markdownToTxt from 'markdown-to-txt'; - -jest.mock('fs', () => ({ - readFileSync: jest.fn(() => 'mock content'), -})); - -const RUN_INTEGRATION_TESTS = process.env.RUN_INTEGRATION_TESTS === 'true'; - -describe('DBSchemaHandler', () => { - describe('Integration Tests', () => { - (RUN_INTEGRATION_TESTS ? describe : describe.skip)( - 'Schema Generation Tests', - () => { - it('should generate schema for blog system', async () => { - const handler = new DBSchemaHandler(); - const context = new BuilderContext( - { - id: 'test', - name: 'test db schema', - version: '1.0.0', - description: 'test db schema', - steps: [], - }, - 'test-id-schema-1', - ); - - const mdFileContent = readFileSync( - './db-requirement.document.md', - 'utf-8', - ); - const plainText = markdownToTxt(mdFileContent); - const result = await handler.run(context, plainText); - console.log(result); - }, 30000); - }, - ); - }); - - describe('Unit Tests', () => { - it('should initialize correctly', () => { - const handler = new DBSchemaHandler(); - expect(handler).toBeDefined(); - expect(handler.id).toBe('OP:DATABASE:SCHEMAS'); - }); - }); -}); diff --git a/backend/src/build-system/__tests__/test-generate-doc.spec.ts b/backend/src/build-system/__tests__/test-generate-doc.spec.ts index ca84f7da..90cc1b26 100644 --- a/backend/src/build-system/__tests__/test-generate-doc.spec.ts +++ b/backend/src/build-system/__tests__/test-generate-doc.spec.ts @@ -1,22 +1,15 @@ -/* eslint-disable no-console */ -import { BuilderContext } from 'src/build-system/context'; import { BuildSequence } from '../types'; -import * as fs from 'fs'; -import * as path from 'path'; -import { writeToFile } from './utils'; +import { executeBuildSequence } from './utils'; +// TODO: adding integration flag describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { - // Generate a unique folder with a timestamp - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const logFolderPath = `./logs/generate-docs-${timestamp}`; - fs.mkdirSync(logFolderPath, { recursive: true }); - it('should execute the full sequence and log results to individual files', async () => { const sequence: BuildSequence = { - id: 'test-sequence', + id: 'test-backend-sequence', version: '1.0.0', - name: 'Test PRD to UX Sequence', - description: 'Testing PRD to UX sequence execution', + name: 'Spotify-like Music Web', + description: 'Users can play music', + databaseType: 'SQLite', steps: [ { id: 'step-1', @@ -35,7 +28,6 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { { id: 'op:UX:SMD', name: 'UX Sitemap Document Node', - requires: ['op:PRD'], }, ], }, @@ -46,7 +38,6 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { { id: 'op:UX:SMS', name: 'UX Sitemap Structure Node', - requires: ['op:UX:SMD'], }, ], }, @@ -57,73 +48,38 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { { id: 'op:UX:DATAMAP:DOC', name: 'UX Data Map Document node', - requires: ['op:UX:SMD'], }, ], }, { id: 'step-5', - name: 'file structure generation', - nodes: [ - { - id: 'op:FILE:STRUCT', - name: 'file structure generation', - requires: ['op:UX:SMD', 'op:UX:DATAMAP:DOC'], - options: { - projectPart: 'frontend', - }, - }, - ], - }, - { - id: 'step-6', - name: 'File_Arch Document', + name: 'UX SMD LEVEL 2 Page Details', nodes: [ { - id: 'op:FILE:ARCH', - name: 'File_Arch', - requires: [ - 'op:FILE:STRUCT', - //TODO: here use datamap doc rather than datamap struct, we have to change this - 'op:UX:DATAMAP:DOC', - ], + id: 'op:UX:SMS:LEVEL2', + name: 'UX SMD LEVEL 2 Page Details Node', }, ], }, ], }; - const context = new BuilderContext(sequence, 'test'); - - // Set input data for context - context.setGlobalContext('projectName', 'spotify like music web'); - context.setGlobalContext('description', 'user can play music'); - context.setGlobalContext('platform', 'web'); - try { - await context.execute(); - - for (const step of sequence.steps) { - for (const node of step.nodes) { - const resultData = await context.getNodeData(node.id); - console.log(resultData); - if (resultData) { - writeToFile(logFolderPath, node.id, resultData); - } - } - } + const result = await executeBuildSequence( + 'test-generate-all-ux-part', + sequence, + ); console.log( 'Sequence completed successfully. Logs stored in:', - logFolderPath, + result.logFolderPath, ); + + if (!result.success) { + throw result.error; + } } catch (error) { console.error('Error during sequence execution:', error); - fs.writeFileSync( - path.join(logFolderPath, 'error.txt'), - `Error: ${error.message}\n${error.stack}`, - 'utf8', - ); throw error; } }, 600000); diff --git a/backend/src/build-system/__tests__/test.fullstack-gen.spec.ts b/backend/src/build-system/__tests__/test.fullstack-gen.spec.ts index 4dd900e3..b4ccad45 100644 --- a/backend/src/build-system/__tests__/test.fullstack-gen.spec.ts +++ b/backend/src/build-system/__tests__/test.fullstack-gen.spec.ts @@ -1,19 +1,8 @@ -/* eslint-disable no-console */ -import { BuilderContext } from 'src/build-system/context'; import { BuildSequence } from '../types'; -import * as fs from 'fs'; -import * as path from 'path'; -import { objectToMarkdown, writeToFile } from './utils'; -import { BuildMonitor } from '../monitor'; +import { executeBuildSequence, objectToMarkdown, writeToFile } from './utils'; -describe('Sequence: PRD -> UXSD -> UXSS -> UXDD -> DATABASE_REQ -> DBSchemas -> Frontend_File_struct -> Frontend_File_arch -> BackendCodeGenerator', () => { - // Generate a unique folder with a timestamp - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const logFolderPath = `./logs/Fullstack_code_generator-${timestamp}`; - fs.mkdirSync(logFolderPath, { recursive: true }); - - it('should execute the frontend and backend code generation sequence and log results to individual files', async () => { - // Define the build sequence up to Backend Code Generator +describe('Build Sequence Test', () => { + it('should execute build sequence successfully', async () => { const sequence: BuildSequence = { id: 'test-backend-sequence', version: '1.0.0', @@ -90,6 +79,11 @@ describe('Sequence: PRD -> UXSD -> UXSS -> UXDD -> DATABASE_REQ -> DBSchemas -> projectPart: 'frontend', }, }, + { + id: 'op:UX:SMS:LEVEL2', + name: 'Level 2 UX Sitemap Structure Node details', + requires: ['op:UX:SMS'], + }, ], }, { @@ -145,114 +139,10 @@ describe('Sequence: PRD -> UXSD -> UXSS -> UXDD -> DATABASE_REQ -> DBSchemas -> }, ], }; - const context = new BuilderContext(sequence, 'test-env'); - const monitor = BuildMonitor.getInstance(); - - try { - console.time('Total Execution Time'); - - await context.execute(); - - console.timeEnd('Total Execution Time'); - - const monitorReport = monitor.generateTextReport(sequence.id); - fs.writeFileSync( - path.join(logFolderPath, 'execution-metrics.txt'), - monitorReport, - 'utf8', - ); - - const sequenceMetrics = monitor.getSequenceMetrics(sequence.id); - if (sequenceMetrics) { - const metricsJson = { - totalDuration: `${sequenceMetrics.duration}ms`, - successRate: `${sequenceMetrics.successRate.toFixed(2)}%`, - totalSteps: sequenceMetrics.totalSteps, - completedSteps: sequenceMetrics.completedSteps, - failedSteps: sequenceMetrics.failedSteps, - totalNodes: sequenceMetrics.totalNodes, - startTime: new Date(sequenceMetrics.startTime).toISOString(), - endTime: new Date(sequenceMetrics.endTime).toISOString(), - }; - - fs.writeFileSync( - path.join(logFolderPath, 'metrics.json'), - JSON.stringify(metricsJson, null, 2), - 'utf8', - ); - - console.log('\nSequence Metrics:'); - console.table(metricsJson); - } - - for (const step of sequence.steps) { - const stepMetrics = sequenceMetrics?.stepMetrics.get(step.id); - for (const node of step.nodes) { - const resultData = await context.getNodeData(node.id); - const nodeMetrics = stepMetrics?.nodeMetrics.get(node.id); - - if (resultData) { - const content = - typeof resultData === 'object' - ? objectToMarkdown(resultData) - : resultData; - - writeToFile(logFolderPath, `${node.name}`, content); - } else { - console.error( - ` Error: Handler ${node.name} failed to produce result data`, - ); - writeToFile( - logFolderPath, - `${node.name}-error`, - objectToMarkdown({ - error: 'No result data', - metrics: nodeMetrics, - }), - ); - } - } - } - - const summary = { - timestamp: new Date().toISOString(), - sequenceId: sequence.id, - sequenceName: sequence.name, - totalExecutionTime: `${sequenceMetrics?.duration}ms`, - successRate: `${sequenceMetrics?.successRate.toFixed(2)}%`, - nodesExecuted: sequenceMetrics?.totalNodes, - completedNodes: sequenceMetrics?.stepMetrics.size, - logFolder: logFolderPath, - }; - - fs.writeFileSync( - path.join(logFolderPath, 'execution-summary.json'), - JSON.stringify(summary, null, 2), - 'utf8', - ); - } catch (error) { - const errorReport = { - error: { - message: error.message, - stack: error.stack, - }, - metrics: monitor.getSequenceMetrics(sequence.id), - timestamp: new Date().toISOString(), - }; - - fs.writeFileSync( - path.join(logFolderPath, 'error-with-metrics.json'), - JSON.stringify(errorReport, null, 2), - 'utf8', - ); - console.error('\nError during sequence execution:'); - console.error(error); - console.error( - '\nError report saved to:', - path.join(logFolderPath, 'error-with-metrics.json'), - ); - throw new Error('Sequence execution failed.'); - } - }, 300000); // Timeout set to 10 minutes + const result = await executeBuildSequence('fullstack-code-gen', sequence); + expect(result.success).toBe(true); + expect(result.metrics).toBeDefined(); + console.log(`Logs saved to: ${result.logFolderPath}`); + }, 300000); }); diff --git a/backend/src/build-system/__tests__/test.model-provider.spec.ts b/backend/src/build-system/__tests__/test.model-provider.spec.ts index f66df109..cd9b9ff1 100644 --- a/backend/src/build-system/__tests__/test.model-provider.spec.ts +++ b/backend/src/build-system/__tests__/test.model-provider.spec.ts @@ -1,9 +1,12 @@ -import { EmbeddingProvider } from "src/common/embedding-provider"; +import { EmbeddingProvider } from 'src/common/embedding-provider'; describe('Model Provider Test', () => { - let embProvider = EmbeddingProvider.getInstance(); - it('should generate a response from the model provider', async () => { - let res = await embProvider.generateEmbResponse("Your text string goes here", "text-embedding-3-small"); - console.log(res); - }); -}); \ No newline at end of file + let embProvider = EmbeddingProvider.getInstance(); + it('should generate a response from the model provider', async () => { + let res = await embProvider.generateEmbResponse( + 'Your text string goes here', + 'text-embedding-3-small', + ); + console.log(res); + }); +}); diff --git a/backend/src/build-system/__tests__/utils.ts b/backend/src/build-system/__tests__/utils.ts index 3400ee69..3702d525 100644 --- a/backend/src/build-system/__tests__/utils.ts +++ b/backend/src/build-system/__tests__/utils.ts @@ -1,6 +1,9 @@ import { Logger } from '@nestjs/common'; import * as fs from 'fs'; import * as path from 'path'; +import { BuildSequence } from '../types'; +import { BuilderContext } from '../context'; +import { BuildMonitor } from '../monitor'; /** * Utility function to write content to a file in a clean, formatted manner. * @param handlerName - The name of the handler. @@ -82,3 +85,134 @@ export function objectToMarkdown(obj: any, depth = 1): string { return markdown; } + +interface TestResult { + success: boolean; + logFolderPath: string; + error?: Error; + metrics?: any; +} + +export async function executeBuildSequence( + name: string, + sequence: BuildSequence, +): Promise { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const logFolderPath = `./logs/${name.toLocaleLowerCase().replaceAll(' ', '-')}-${timestamp}`; + fs.mkdirSync(logFolderPath, { recursive: true }); + + const context = new BuilderContext(sequence, 'test-env'); + const monitor = BuildMonitor.getInstance(); + + try { + console.time('Total Execution Time'); + await context.execute(); + console.timeEnd('Total Execution Time'); + + const monitorReport = monitor.generateTextReport(sequence.id); + fs.writeFileSync( + path.join(logFolderPath, 'execution-metrics.txt'), + monitorReport, + 'utf8', + ); + + const sequenceMetrics = monitor.getSequenceMetrics(sequence.id); + if (sequenceMetrics) { + const metricsJson = { + totalDuration: `${sequenceMetrics.duration}ms`, + successRate: `${sequenceMetrics.successRate.toFixed(2)}%`, + totalSteps: sequenceMetrics.totalSteps, + completedSteps: sequenceMetrics.completedSteps, + failedSteps: sequenceMetrics.failedSteps, + totalNodes: sequenceMetrics.totalNodes, + startTime: new Date(sequenceMetrics.startTime).toISOString(), + endTime: new Date(sequenceMetrics.endTime).toISOString(), + }; + + fs.writeFileSync( + path.join(logFolderPath, 'metrics.json'), + JSON.stringify(metricsJson, null, 2), + 'utf8', + ); + + console.log('\nSequence Metrics:'); + console.table(metricsJson); + } + + for (const step of sequence.steps) { + const stepMetrics = sequenceMetrics?.stepMetrics.get(step.id); + for (const node of step.nodes) { + const resultData = await context.getNodeData(node.id); + const nodeMetrics = stepMetrics?.nodeMetrics.get(node.id); + + if (resultData) { + const content = + typeof resultData === 'object' + ? objectToMarkdown(resultData) + : resultData; + writeToFile(logFolderPath, `${node.name}`, content); + } else { + console.error( + `Error: Handler ${node.name} failed to produce result data`, + ); + writeToFile( + logFolderPath, + `${node.name}-error`, + objectToMarkdown({ + error: 'No result data', + metrics: nodeMetrics, + }), + ); + } + } + } + + const summary = { + timestamp: new Date().toISOString(), + sequenceId: sequence.id, + sequenceName: sequence.name, + totalExecutionTime: `${sequenceMetrics?.duration}ms`, + successRate: `${sequenceMetrics?.successRate.toFixed(2)}%`, + nodesExecuted: sequenceMetrics?.totalNodes, + completedNodes: sequenceMetrics?.stepMetrics.size, + logFolder: logFolderPath, + }; + + fs.writeFileSync( + path.join(logFolderPath, 'execution-summary.json'), + JSON.stringify(summary, null, 2), + 'utf8', + ); + + return { + success: true, + logFolderPath, + metrics: sequenceMetrics, + }; + } catch (error) { + const errorReport = { + error: { + message: error.message, + stack: error.stack, + }, + metrics: monitor.getSequenceMetrics(sequence.id), + timestamp: new Date().toISOString(), + }; + + fs.writeFileSync( + path.join(logFolderPath, 'error-with-metrics.json'), + JSON.stringify(errorReport, null, 2), + 'utf8', + ); + + console.error('\nError during sequence execution:'); + console.error(error); + + return { + success: false, + logFolderPath, + error: error as Error, + metrics: monitor.getSequenceMetrics(sequence.id), + }; + } +} diff --git a/backend/src/build-system/handlers/file-manager/file-arch/index.ts b/backend/src/build-system/handlers/file-manager/file-arch/index.ts index f0850650..1b82c76a 100644 --- a/backend/src/build-system/handlers/file-manager/file-arch/index.ts +++ b/backend/src/build-system/handlers/file-manager/file-arch/index.ts @@ -31,7 +31,7 @@ export class FileArchGenerateHandler implements BuildHandler { } const prompt = generateFileArchPrompt( - JSON.stringify(fileStructure, null, 2), + JSON.stringify(fileStructure.jsonFileStructure, null, 2), JSON.stringify(datamapDoc, null, 2), ); diff --git a/backend/src/build-system/handlers/file-manager/file-structure/index.ts b/backend/src/build-system/handlers/file-manager/file-structure/index.ts index e3bd5b51..d9989cda 100644 --- a/backend/src/build-system/handlers/file-manager/file-structure/index.ts +++ b/backend/src/build-system/handlers/file-manager/file-structure/index.ts @@ -1,4 +1,9 @@ -import { BuildHandler, BuildOpts, BuildResult } from 'src/build-system/types'; +import { + BuildHandler, + BuildOpts, + BuildResult, + FileStructOutput, +} from 'src/build-system/types'; import { BuilderContext } from 'src/build-system/context'; import { prompts } from './prompt'; import { Logger } from '@nestjs/common'; @@ -8,7 +13,7 @@ import { removeCodeBlockFences } from 'src/build-system/utils/strings'; * FileStructureHandler is responsible for generating the project's file and folder structure * based on the provided documentation. */ -export class FileStructureHandler implements BuildHandler { +export class FileStructureHandler implements BuildHandler { readonly id = 'op:FILE:STRUCT'; private readonly logger: Logger = new Logger('FileStructureHandler'); @@ -21,7 +26,7 @@ export class FileStructureHandler implements BuildHandler { async run( context: BuilderContext, opts?: BuildOpts, - ): Promise> { + ): Promise> { this.logger.log('Generating File Structure Document...'); // Retrieve projectName from context @@ -153,7 +158,10 @@ export class FileStructureHandler implements BuildHandler { return { success: true, - data: removeCodeBlockFences(fileStructureJsonContent), + data: { + fileStructure: removeCodeBlockFences(fileStructureContent), + jsonFileStructure: removeCodeBlockFences(fileStructureJsonContent), + }, }; } } diff --git a/backend/src/build-system/handlers/ux/sitemap-structure/index.ts b/backend/src/build-system/handlers/ux/sitemap-structure/index.ts index 758ca813..4d080990 100644 --- a/backend/src/build-system/handlers/ux/sitemap-structure/index.ts +++ b/backend/src/build-system/handlers/ux/sitemap-structure/index.ts @@ -42,92 +42,3 @@ export class UXSitemapStructureHandler implements BuildHandler { }; } } -export class Level2UXSitemapStructureHandler implements BuildHandler { - readonly id = 'op:UX:SMS:LEVEL2'; - readonly logger = new Logger('Level2UXSitemapStructureHandler'); - - async run(context: BuilderContext): Promise> { - this.logger.log('Generating Level 2 UX Sitemap Structure Document...'); - - const projectName = - context.getGlobalContext('projectName') || 'Default Project Name'; - const sitemapDoc = context.getNodeData('op:UX:SMS'); - const uxStructureDoc = context.getNodeData('op:UX:SMS'); - - if (!projectName || !sitemapDoc || !uxStructureDoc) { - return { - success: false, - data: 'Missing required arguments: projectName, sitemapDoc, or uxStructureDoc.', - }; - } - - // Extract sections from the UX Structure Document - const sections = this.extractAllSections(uxStructureDoc); - - if (sections.length === 0) { - this.logger.error( - 'No valid sections found in the UX Structure Document.', - ); - return { - success: false, - data: 'No valid sections found in the UX Structure Document.', - }; - } - - // Process each section with the refined Level 2 prompt - const modelProvider = ModelProvider.getInstance(); - const refinedSections = []; - - for (const section of sections) { - const prompt = prompts.generateLevel2UXSiteMapStructrePrompt( - projectName, - section.content, - sitemapDoc, - 'web', // TODO: Replace with dynamic platform if necessary - ); - - const refinedContent = await modelProvider.chatSync({ - model: 'gpt-4o-mini', - messages: [{ content: prompt, role: 'system' }], - }); - - refinedSections.push({ - title: section.title, - content: refinedContent, - }); - } - - // Combine the refined sections into the final document - const refinedDocument = refinedSections - .map((section) => `## **${section.title}**\n${section.content}`) - .join('\n\n'); - - this.logger.log(refinedDocument); - - return { - success: true, - data: removeCodeBlockFences(refinedDocument), - }; - } - - /** - * Extracts all sections from a given text. - * @param text The UX Structure Document content. - * @returns Array of extracted sections with title and content. - */ - private extractAllSections( - text: string, - ): Array<{ title: string; content: string }> { - const regex = /## \*\*(\d+\.\s.*)\*\*([\s\S]*?)(?=\n## \*\*|$)/g; - const sections = []; - let match; - - while ((match = regex.exec(text)) !== null) { - const title = match[1].trim(); - const content = match[2].trim(); - sections.push({ title, content }); - } - - return sections; - } -} diff --git a/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts b/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts new file mode 100644 index 00000000..28543cd0 --- /dev/null +++ b/backend/src/build-system/handlers/ux/sitemap-structure/sms-page.ts @@ -0,0 +1,96 @@ +import { Logger } from '@nestjs/common'; +import { BuilderContext } from 'src/build-system/context'; +import { BuildHandler, BuildResult } from 'src/build-system/types'; +import { ModelProvider } from 'src/common/model-provider'; +import { prompts } from './prompt'; +import { removeCodeBlockFences } from 'src/build-system/utils/strings'; + +export class Level2UXSitemapStructureHandler implements BuildHandler { + readonly id = 'op:UX:SMS:LEVEL2'; + readonly logger = new Logger('Level2UXSitemapStructureHandler'); + + async run(context: BuilderContext): Promise> { + this.logger.log('Generating Level 2 UX Sitemap Structure Document...'); + + const projectName = + context.getGlobalContext('projectName') || 'Default Project Name'; + const sitemapDoc = context.getNodeData('op:UX:SMS'); + const uxStructureDoc = context.getNodeData('op:UX:SMS'); + + if (!projectName || !sitemapDoc || !uxStructureDoc) { + return { + success: false, + data: 'Missing required arguments: projectName, sitemapDoc, or uxStructureDoc.', + }; + } + + // Extract sections from the UX Structure Document + const sections = this.extractAllSections(uxStructureDoc); + + if (sections.length === 0) { + this.logger.error( + 'No valid sections found in the UX Structure Document.', + ); + return { + success: false, + data: 'No valid sections found in the UX Structure Document.', + }; + } + + // Process each section with the refined Level 2 prompt + const modelProvider = ModelProvider.getInstance(); + const refinedSections = []; + + for (const section of sections) { + const prompt = prompts.generateLevel2UXSiteMapStructrePrompt( + projectName, + section.content, + sitemapDoc, + 'web', // TODO: Replace with dynamic platform if necessary + ); + + const refinedContent = await modelProvider.chatSync({ + model: 'gpt-4o-mini', + messages: [{ content: prompt, role: 'system' }], + }); + + refinedSections.push({ + title: section.title, + content: refinedContent, + }); + } + + // Combine the refined sections into the final document + const refinedDocument = refinedSections + .map((section) => `## **${section.title}**\n${section.content}`) + .join('\n\n'); + + this.logger.log(refinedDocument); + + return { + success: true, + data: removeCodeBlockFences(refinedDocument), + }; + } + + /** + * Extracts all sections from a given text. + * @param text The UX Structure Document content. + * @returns Array of extracted sections with title and content. + */ + private extractAllSections( + text: string, + ): Array<{ title: string; content: string }> { + const regex = /## \*\*(\d+\.\s.*)\*\*([\s\S]*?)(?=\n## \*\*|$)/g; + const sections = []; + let match; + + while ((match = regex.exec(text)) !== null) { + const title = match[1].trim(); + const content = match[2].trim(); + sections.push({ title, content }); + } + + return sections; + } +} diff --git a/backend/src/build-system/hanlder-manager.ts b/backend/src/build-system/hanlder-manager.ts index f41927d6..8434956e 100644 --- a/backend/src/build-system/hanlder-manager.ts +++ b/backend/src/build-system/hanlder-manager.ts @@ -12,6 +12,7 @@ import { DatabaseRequirementHandler } from './handlers/database/requirements-doc import { FileGeneratorHandler } from './handlers/file-manager/file-generate'; import { BackendRequirementHandler } from './handlers/backend/requirements-document'; import { BackendFileReviewHandler } from './handlers/backend/file-review/file-review'; +import { Level2UXSitemapStructureHandler } from './handlers/ux/sitemap-structure/sms-page'; /** * Manages the registration and retrieval of build handlers in the system @@ -35,6 +36,7 @@ export class BuildHandlerManager { new ProjectInitHandler(), new PRDHandler(), new UXSitemapStructureHandler(), + new Level2UXSitemapStructureHandler(), new UXDatamapHandler(), new UXSMDHandler(), new FileStructureHandler(), diff --git a/backend/src/build-system/types.ts b/backend/src/build-system/types.ts index 45f9882d..ae1066c5 100644 --- a/backend/src/build-system/types.ts +++ b/backend/src/build-system/types.ts @@ -74,6 +74,19 @@ export interface BuildHandler { run(context: BuilderContext, opts?: BuildOpts): Promise>; } +export interface FileStructOutput { + /** + * Tree File Structure: + * src: + * - components: + */ + fileStructure: string; + /** + * Example JSON file structure: + * + */ + jsonFileStructure: string; +} export interface NodeOutputMap { 'op:DATABASE_REQ': string; 'op:PRD': string; @@ -81,7 +94,7 @@ export interface NodeOutputMap { 'op:UX:SMS': string; 'op:UX:SMS:LEVEL2': string; 'op:UX:DATAMAP:DOC': string; - 'op:FILE:STRUCT': string; + 'op:FILE:STRUCT': FileStructOutput; 'op:FILE:ARCH': string; 'op:FILE:GENERATE': string; 'op:BACKEND:CODE': string; From 3eb7273619b7d983f35ea9551a2a495e4ee2c476 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:44:52 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- .../src/build-system/__tests__/test.model-provider.spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/build-system/__tests__/test.model-provider.spec.ts b/backend/src/build-system/__tests__/test.model-provider.spec.ts index c6dd53ca..66314492 100644 --- a/backend/src/build-system/__tests__/test.model-provider.spec.ts +++ b/backend/src/build-system/__tests__/test.model-provider.spec.ts @@ -1,10 +1,9 @@ import { EmbeddingProvider } from 'src/common/embedding-provider'; describe('Model Provider Test', () => { - - let embProvider = EmbeddingProvider.getInstance(); + const embProvider = EmbeddingProvider.getInstance(); it('should generate a response from the model provider', async () => { - let res = await embProvider.generateEmbResponse( + const res = await embProvider.generateEmbResponse( 'Your text string goes here', 'text-embedding-3-small', );