Enhanced core ai call for FULL parallel section generation.

This commit is contained in:
ValueOn AG 2026-01-06 17:53:55 +01:00
parent 657ad98e75
commit 30ad4bb762
4 changed files with 9843 additions and 116 deletions

File diff suppressed because it is too large Load diff

View file

@ -1531,107 +1531,89 @@ class StructureFiller:
maxConcurrent = self._getMaxConcurrentGeneration(options)
sectionSemaphore = asyncio.Semaphore(maxConcurrent)
# Helper function to calculate overall progress
def calculateOverallProgress(chapterIndex, totalChapters, sectionIndex, totalSections):
"""Calculate overall progress: 0.0 to 1.0"""
if totalChapters == 0:
return 1.0
# Collect ALL sections from ALL chapters for fully parallel processing
# Each task carries: (docId, chapterId, chapterTitle, sectionIndex, section, docLanguage)
allSectionTasks = []
totalSections = len(all_sections_list)
completedSections = [0] # Mutable counter for progress tracking
# Progress from completed chapters (0 to chapterIndex-1)
completedChaptersProgress = chapterIndex / totalChapters
# Progress from current chapter (sectionIndex / totalSections)
currentChapterProgress = (sectionIndex / totalSections) / totalChapters if totalSections > 0 else 0
return min(1.0, completedChaptersProgress + currentChapterProgress)
# Process chapters sequentially with chapter-level progress
chapterIndex = 0
for doc in chapterStructure.get("documents", []):
docId = doc.get("id", "unknown")
# Get language for this specific document
docLanguage = self._getDocumentLanguage(chapterStructure, docId)
for chapter in doc.get("chapters", []):
chapterIndex += 1
chapterId = chapter.get("id", "unknown")
chapterTitle = chapter.get("title", "Untitled Chapter")
sections = chapter.get("sections", [])
totalSections = len(sections)
chapterSectionCount = len(sections)
# Start chapter operation
chapterOperationId = f"{fillOperationId}_chapter_{chapterId}"
self.services.chat.progressLogStart(
chapterOperationId,
"Chapter Generation",
f"Chapter {chapterIndex}/{totalChapters}",
chapterTitle,
parentOperationId=fillOperationId
)
# Process sections within chapter in parallel with concurrency control
sectionTasks = []
for sectionIndex, section in enumerate(sections):
# Create task wrapper with semaphore for parallel processing
async def processSectionWithSemaphore(section, sectionIndex, totalSections, chapterIndex, totalChapters, chapterId, chapterOperationId, fillOperationId, contentParts, userPrompt, all_sections_list, docLanguage, calculateOverallProgress):
allSectionTasks.append({
"docId": docId,
"chapterId": chapterId,
"chapterTitle": chapterTitle,
"sectionIndex": sectionIndex,
"chapterSectionCount": chapterSectionCount,
"section": section,
"docLanguage": docLanguage
})
logger.info(f"Starting FULLY PARALLEL section generation: {totalSections} sections across {totalChapters} chapters")
# Create task wrapper for each section with progress tracking
async def processSectionWithSemaphore(taskInfo):
checkWorkflowStopped(self.services)
async with sectionSemaphore:
return await self._processSingleSection(
section=section,
sectionIndex=sectionIndex,
totalSections=totalSections,
chapterIndex=chapterIndex,
result = await self._processSingleSection(
section=taskInfo["section"],
sectionIndex=taskInfo["sectionIndex"],
totalSections=taskInfo["chapterSectionCount"],
chapterIndex=0, # Not used for sequential logic anymore
totalChapters=totalChapters,
chapterId=chapterId,
chapterOperationId=chapterOperationId,
chapterId=taskInfo["chapterId"],
chapterOperationId=f"{fillOperationId}_chapter_{taskInfo['chapterId']}",
fillOperationId=fillOperationId,
contentParts=contentParts,
userPrompt=userPrompt,
all_sections_list=all_sections_list,
language=docLanguage, # Use document-specific language
calculateOverallProgress=calculateOverallProgress
language=taskInfo["docLanguage"],
calculateOverallProgress=lambda *args: completedSections[0] / totalSections if totalSections > 0 else 1.0
)
task = processSectionWithSemaphore(
section, sectionIndex, totalSections, chapterIndex, totalChapters, chapterId, chapterOperationId, fillOperationId, contentParts, userPrompt, all_sections_list, docLanguage, calculateOverallProgress
)
sectionTasks.append((sectionIndex, section, task))
# Execute all section tasks in parallel with concurrency control
if sectionTasks:
# Create list of tasks (without indices for gather)
tasks = [task for _, _, task in sectionTasks]
# Execute in parallel with error handling
results = await asyncio.gather(*tasks, return_exceptions=True)
# Process results in order and assign elements to sections
for (originalIndex, originalSection, _), result in zip(sectionTasks, results):
if isinstance(result, Exception):
logger.error(f"Error processing section {originalSection.get('id')}: {str(result)}")
# Set error element
originalSection["elements"] = [{
"type": "error",
"message": f"Error processing section: {str(result)}",
"sectionId": originalSection.get("id")
}]
else:
# Assign elements to section in correct order
# CRITICAL: Always assign elements, even if empty list
# This ensures sections always have an elements field for validation
originalSection["elements"] = result if result is not None else []
# Finish chapter operation after all sections processed
self.services.chat.progressLogFinish(chapterOperationId, True)
# Update overall progress after chapter completion
overallProgress = chapterIndex / totalChapters if totalChapters > 0 else 1.0
# Update progress after each section completes
completedSections[0] += 1
overallProgress = completedSections[0] / totalSections if totalSections > 0 else 1.0
sectionId = taskInfo["section"].get("id", "unknown")
self.services.chat.progressLogUpdate(
fillOperationId,
overallProgress,
f"Chapter {chapterIndex}/{totalChapters} completed: {chapterTitle}"
f"Section {completedSections[0]}/{totalSections} completed: {sectionId}"
)
return result
# Create all tasks
tasks = [processSectionWithSemaphore(taskInfo) for taskInfo in allSectionTasks]
# Execute ALL sections in parallel with concurrency control
if tasks:
results = await asyncio.gather(*tasks, return_exceptions=True)
# Assign results back to sections
for taskInfo, result in zip(allSectionTasks, results):
section = taskInfo["section"]
if isinstance(result, Exception):
logger.error(f"Error processing section {section.get('id')}: {str(result)}")
section["elements"] = [{
"type": "error",
"message": f"Error processing section: {str(result)}",
"sectionId": section.get("id")
}]
else:
section["elements"] = result if result is not None else []
logger.info(f"Completed FULLY PARALLEL section generation: {totalSections} sections")
return chapterStructure
def _addContentPartsMetadata(
@ -2224,23 +2206,23 @@ Context showing structure hierarchy with cut point:
```
Overlap Requirement:
To ensure proper merging, your response MUST start by repeating the cut part and the full part before (same level) shown below, then continue with new content.
To ensure proper merging, your response MUST start EXACTLY with the overlap context shown below, then continue with new content.
Overlap context (cut part and full part before at same level):
Overlap context (start your response with this exact text):
```json
{overlapContext if overlapContext else "No overlap context available"}
```
TASK:
1. Start your response by repeating the overlap context shown above (cut part and full part before at same level)
2. Complete the incomplete element shown in the context above (marked with CUT POINT)
3. Continue generating the remaining content following the JSON structure template above
1. Start your response EXACTLY with the overlap context shown above (character by character)
2. Continue seamlessly from where the overlap context ends
3. Complete the remaining content following the JSON structure template above
4. Return ONLY valid JSON following the structure template - no overlap/continuation wrapper objects
CRITICAL:
- Your response must be valid JSON matching the structure template above
- Start with overlap context (cut part and full part before at same level) then continue seamlessly
- Complete the incomplete element and continue with remaining elements"""
- Your response MUST begin with the exact overlap context text (this enables automatic merging)
- Continue seamlessly after the overlap context with new content
- Your response must be valid JSON matching the structure template above"""
return continuationPrompt
def _extractAndMergeMultipleJsonBlocks(self, responseText: str, contentType: str, sectionId: str) -> List[Dict[str, Any]]:

View file

@ -154,23 +154,23 @@ Context showing structure hierarchy with cut point:
```
Overlap Requirement:
To ensure proper merging, your response MUST start by repeating the cut part and the full part before (same level) shown below, then continue with new content.
To ensure proper merging, your response MUST start EXACTLY with the overlap context shown below, then continue with new content.
Overlap context (cut part and full part before at same level):
Overlap context (start your response with this exact text):
```json
{overlapContext if overlapContext else "No overlap context available"}
```
TASK:
1. Start your response by repeating the overlap context shown above (cut part and full part before at same level)
2. Complete the incomplete element shown in the context above (marked with CUT POINT)
3. Continue generating the remaining content following the JSON structure template above
1. Start your response EXACTLY with the overlap context shown above (character by character)
2. Continue seamlessly from where the overlap context ends
3. Complete the remaining content following the JSON structure template above
4. Return ONLY valid JSON following the structure template - no overlap/continuation wrapper objects
CRITICAL:
- Your response must be valid JSON matching the structure template above
- Start with overlap context (cut part and full part before at same level) then continue seamlessly
- Complete the incomplete element and continue with remaining elements"""
- Your response MUST begin with the exact overlap context text (this enables automatic merging)
- Continue seamlessly after the overlap context with new content
- Your response must be valid JSON matching the structure template above"""
return continuationPrompt
# Call AI with looping support

View file

@ -364,23 +364,23 @@ Context showing structure hierarchy with cut point:
```
Overlap Requirement:
To ensure proper merging, your response MUST start by repeating the cut part and the full part before (same level) shown below, then continue with new content.
To ensure proper merging, your response MUST start EXACTLY with the overlap context shown below, then continue with new content.
Overlap context (cut part and full part before at same level):
Overlap context (start your response with this exact text):
```json
{overlapContext if overlapContext else "No overlap context available"}
```
TASK:
1. Start your response by repeating the overlap context shown above (cut part and full part before at same level)
2. Complete the incomplete element shown in the context above (marked with CUT POINT)
3. Continue generating the remaining content following the JSON structure template above
1. Start your response EXACTLY with the overlap context shown above (character by character)
2. Continue seamlessly from where the overlap context ends
3. Complete the remaining content following the JSON structure template above
4. Return ONLY valid JSON following the structure template - no overlap/continuation wrapper objects
CRITICAL:
- Your response must be valid JSON matching the structure template above
- Start with overlap context (cut part and full part before at same level) then continue seamlessly
- Complete the incomplete element and continue with remaining elements"""
- Your response MUST begin with the exact overlap context text (this enables automatic merging)
- Continue seamlessly after the overlap context with new content
- Your response must be valid JSON matching the structure template above"""
return continuationPrompt
# Use generic looping system with code_structure use case
@ -811,23 +811,23 @@ Context showing structure hierarchy with cut point:
```
Overlap Requirement:
To ensure proper merging, your response MUST start by repeating the cut part and the full part before (same level) shown below, then continue with new content.
To ensure proper merging, your response MUST start EXACTLY with the overlap context shown below, then continue with new content.
Overlap context (cut part and full part before at same level):
Overlap context (start your response with this exact text):
```json
{overlapContext if overlapContext else "No overlap context available"}
```
TASK:
1. Start your response by repeating the overlap context shown above (cut part and full part before at same level)
2. Complete the incomplete element shown in the context above (marked with CUT POINT)
3. Continue generating the remaining content following the JSON structure template above
1. Start your response EXACTLY with the overlap context shown above (character by character)
2. Continue seamlessly from where the overlap context ends
3. Complete the remaining content following the JSON structure template above
4. Return ONLY valid JSON following the structure template - no overlap/continuation wrapper objects
CRITICAL:
- Your response must be valid JSON matching the structure template above
- Start with overlap context (cut part and full part before at same level) then continue seamlessly
- Complete the incomplete element and continue with remaining elements"""
- Your response MUST begin with the exact overlap context text (this enables automatic merging)
- Continue seamlessly after the overlap context with new content
- Your response must be valid JSON matching the structure template above"""
return continuationPrompt
# Use generic looping system with code_content use case