Enhanced core ai call for FULL parallel section generation.
This commit is contained in:
parent
657ad98e75
commit
30ad4bb762
4 changed files with 9843 additions and 116 deletions
9745
modules/services/serviceAi/merge_1.txt
Normal file
9745
modules/services/serviceAi/merge_1.txt
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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]]:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue