AI Generator Loop with context added
This commit is contained in:
parent
1917c00721
commit
6b7094c84d
3 changed files with 103 additions and 5 deletions
|
|
@ -119,9 +119,9 @@ class SubDocumentGeneration:
|
||||||
# Update progress - starting AI processing
|
# Update progress - starting AI processing
|
||||||
progressLogger.updateProgress(operationId, 0.3, "AI processing")
|
progressLogger.updateProgress(operationId, 0.3, "AI processing")
|
||||||
|
|
||||||
# Process documents with format-specific prompt using JSON mode
|
# Process documents with format-specific prompt using JSON mode with chunking
|
||||||
# This ensures structured JSON output instead of text
|
# This ensures structured JSON output instead of text and handles large documents
|
||||||
aiResponseJson = await self._callAiJson(extractionPrompt, documents, options)
|
aiResponseJson = await self._callAiJsonWithChunking(extractionPrompt, documents, options, progressLogger, operationId)
|
||||||
|
|
||||||
# Update progress - AI processing completed
|
# Update progress - AI processing completed
|
||||||
progressLogger.updateProgress(operationId, 0.6, "Processing done")
|
progressLogger.updateProgress(operationId, 0.6, "Processing done")
|
||||||
|
|
@ -648,6 +648,90 @@ class SubDocumentGeneration:
|
||||||
# Process documents with JSON merging
|
# Process documents with JSON merging
|
||||||
return await self.documentProcessor.processDocumentsPerChunkJson(documents, prompt, options)
|
return await self.documentProcessor.processDocumentsPerChunkJson(documents, prompt, options)
|
||||||
|
|
||||||
|
async def _callAiJsonWithChunking(
|
||||||
|
self,
|
||||||
|
prompt: str,
|
||||||
|
documents: Optional[List[ChatDocument]],
|
||||||
|
options: AiCallOptions,
|
||||||
|
progressLogger,
|
||||||
|
operationId: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Handle AI calls with document processing for JSON output using chunking for large documents.
|
||||||
|
Supports continuation when documents are too large for single AI call.
|
||||||
|
"""
|
||||||
|
# Initialize the document structure
|
||||||
|
completeDocument = {
|
||||||
|
"metadata": {"title": "Generated Document"},
|
||||||
|
"sections": [],
|
||||||
|
"continue": True
|
||||||
|
}
|
||||||
|
|
||||||
|
continuationContext = None
|
||||||
|
chunkCount = 0
|
||||||
|
maxChunks = 10 # Prevent infinite loops
|
||||||
|
|
||||||
|
while completeDocument.get("continue", False) and chunkCount < maxChunks:
|
||||||
|
chunkCount += 1
|
||||||
|
logger.info(f"Processing generation chunk {chunkCount}")
|
||||||
|
|
||||||
|
# Update progress
|
||||||
|
progressLogger.updateProgress(operationId, 0.3 + (chunkCount * 0.3 / maxChunks), f"Generating chunk {chunkCount}")
|
||||||
|
|
||||||
|
# Prepare the chunk prompt
|
||||||
|
if continuationContext:
|
||||||
|
chunkPrompt = f"""
|
||||||
|
{prompt}
|
||||||
|
|
||||||
|
CONTINUATION CONTEXT:
|
||||||
|
- Last completed section: {continuationContext.get('last_section_id', 'none')}
|
||||||
|
- Last completed element index: {continuationContext.get('last_element_index', 0)}
|
||||||
|
- Remaining requirements: {continuationContext.get('remaining_requirements', 'complete the document')}
|
||||||
|
|
||||||
|
Continue generating the document from where you left off. Include all previously generated content and add the remaining sections.
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
chunkPrompt = prompt
|
||||||
|
|
||||||
|
# Call AI for this chunk using the existing document processor
|
||||||
|
aiResponseJson = await self.documentProcessor.processDocumentsPerChunkJson(documents, chunkPrompt, options)
|
||||||
|
|
||||||
|
# Validate JSON response
|
||||||
|
if not isinstance(aiResponseJson, dict):
|
||||||
|
raise Exception("AI response is not valid JSON document structure")
|
||||||
|
|
||||||
|
# Merge the chunk with the complete document
|
||||||
|
if chunkCount == 1:
|
||||||
|
# First chunk - use as base
|
||||||
|
completeDocument = aiResponseJson
|
||||||
|
else:
|
||||||
|
# Subsequent chunks - merge sections
|
||||||
|
if "sections" in aiResponseJson:
|
||||||
|
# Find the last section ID from continuation context
|
||||||
|
lastSectionId = continuationContext.get('last_section_id', '') if continuationContext else ''
|
||||||
|
|
||||||
|
# Add new sections after the last completed one
|
||||||
|
newSections = []
|
||||||
|
for section in aiResponseJson["sections"]:
|
||||||
|
if section.get("id") != lastSectionId:
|
||||||
|
newSections.append(section)
|
||||||
|
|
||||||
|
completeDocument["sections"].extend(newSections)
|
||||||
|
|
||||||
|
# Check if we need to continue
|
||||||
|
if aiResponseJson.get("continue", False):
|
||||||
|
continuationContext = aiResponseJson.get("continuation_context", {})
|
||||||
|
logger.info(f"Document generation needs continuation: {continuationContext}")
|
||||||
|
else:
|
||||||
|
completeDocument["continue"] = False
|
||||||
|
logger.info("Document generation completed")
|
||||||
|
|
||||||
|
if chunkCount >= maxChunks:
|
||||||
|
logger.warning(f"Document generation stopped after {maxChunks} chunks (max limit reached)")
|
||||||
|
completeDocument["continue"] = False
|
||||||
|
|
||||||
|
return completeDocument
|
||||||
|
|
||||||
async def _analyzePromptIntent(self, prompt: str, ai_service=None) -> Dict[str, Any]:
|
async def _analyzePromptIntent(self, prompt: str, ai_service=None) -> Dict[str, Any]:
|
||||||
"""Use AI to analyze user prompt and determine processing requirements."""
|
"""Use AI to analyze user prompt and determine processing requirements."""
|
||||||
if not ai_service:
|
if not ai_service:
|
||||||
|
|
|
||||||
|
|
@ -581,9 +581,19 @@ CRITICAL: The AI MUST generate content using the CANONICAL JSON FORMAT with this
|
||||||
],
|
],
|
||||||
"order": 3
|
"order": 3
|
||||||
}}
|
}}
|
||||||
]
|
],
|
||||||
|
"continue": false
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
IMPORTANT CHUNKING LOGIC:
|
||||||
|
- If the document is too large to generate completely in one response, set "continue": true
|
||||||
|
- When "continue": true, include a "continuation_context" field with:
|
||||||
|
- "last_section_id": "id of the last completed section"
|
||||||
|
- "last_element_index": "index of the last completed element in that section"
|
||||||
|
- "remaining_requirements": "brief description of what still needs to be generated"
|
||||||
|
- The AI will be called again with this context to continue generation
|
||||||
|
- Only set "continue": false when the document is completely generated
|
||||||
|
|
||||||
The AI should NOT create format-specific structures like "sheets" or "columns" - only use the canonical format with "sections" and "elements".
|
The AI should NOT create format-specific structures like "sheets" or "columns" - only use the canonical format with "sections" and "elements".
|
||||||
|
|
||||||
Write the instructions as plain text, not JSON. Start with "Generate JSON content that..." and provide clear, actionable instructions for creating structured JSON data in the canonical format.
|
Write the instructions as plain text, not JSON. Start with "Generate JSON content that..." and provide clear, actionable instructions for creating structured JSON data in the canonical format.
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,14 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def generateTaskPlanningPrompt(services, context: Any) -> PromptBundle:
|
def generateTaskPlanningPrompt(services, context: Any) -> PromptBundle:
|
||||||
"""Define placeholders first, then the template; return PromptBundle."""
|
"""Define placeholders first, then the template; return PromptBundle."""
|
||||||
|
# Extract user language from services
|
||||||
|
userLanguage = getattr(services, 'currentUserLanguage', None) or 'en'
|
||||||
|
|
||||||
placeholders: List[PromptPlaceholder] = [
|
placeholders: List[PromptPlaceholder] = [
|
||||||
PromptPlaceholder(label="USER_PROMPT", content=extractUserPrompt(context), summaryAllowed=False),
|
PromptPlaceholder(label="USER_PROMPT", content=extractUserPrompt(context), summaryAllowed=False),
|
||||||
PromptPlaceholder(label="AVAILABLE_DOCUMENTS_SUMMARY", content=extractAvailableDocumentsSummary(services, context), summaryAllowed=True),
|
PromptPlaceholder(label="AVAILABLE_DOCUMENTS_SUMMARY", content=extractAvailableDocumentsSummary(services, context), summaryAllowed=True),
|
||||||
PromptPlaceholder(label="WORKFLOW_HISTORY", content=extractWorkflowHistory(services, context), summaryAllowed=True),
|
PromptPlaceholder(label="WORKFLOW_HISTORY", content=extractWorkflowHistory(services, context), summaryAllowed=True),
|
||||||
|
PromptPlaceholder(label="USER_LANGUAGE", content=userLanguage, summaryAllowed=False),
|
||||||
]
|
]
|
||||||
|
|
||||||
template = """# Task Planning
|
template = """# Task Planning
|
||||||
|
|
@ -71,7 +75,7 @@ Break down user requests into logical, executable task steps.
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"overview": "Brief description of the overall plan",
|
"overview": "Brief description of the overall plan",
|
||||||
"userMessage": "User-friendly message explaining the task plan (use the user's detected language from intent)",
|
"userMessage": "User-friendly message explaining the task plan (use {{KEY:USER_LANGUAGE}} language)",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"id": "task_1",
|
"id": "task_1",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue