Cleaned up Literature folder

This commit is contained in:
Richard Kranendonk 2026-05-18 12:48:01 +02:00
parent 73a6380034
commit fe5eda4e05
586 changed files with 53911 additions and 2475 deletions

View file

@ -0,0 +1,356 @@
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field
from enum import Enum
import json
import re
from datetime import datetime
import uuid
class SlotType(Enum):
TEXT = "text"
NUMBER = "number"
EMAIL = "email"
PHONE = "phone"
DATE = "date"
BOOLEAN = "boolean"
CHOICE = "choice"
class ValidationType(Enum):
REGEX = "regex"
RANGE = "range"
LENGTH = "length"
CHOICES = "choices"
CUSTOM = "custom"
class QuestionType(Enum):
INITIAL = "initial"
CLARIFICATION = "clarification"
VALIDATION_ERROR = "validation_error"
CONFIRMATION = "confirmation"
@dataclass
class SlotValidation:
validation_type: ValidationType
validation_rule: Dict[str, Any]
error_message: str
@dataclass
class SlotQuestion:
question_type: QuestionType
question_text: str
follow_up_text: Optional[str] = None
context_conditions: Optional[Dict[str, Any]] = None
@dataclass
class SlotDefinition:
slot_name: str
slot_type: SlotType
is_required: bool = True
priority: int = 1
depends_on_slot: Optional[str] = None
dependency_value: Optional[List[str]] = None
validations: List[SlotValidation] = field(default_factory=list)
questions: List[SlotQuestion] = field(default_factory=list)
@dataclass
class ConversationSession:
session_id: str
user_id: str
flow_id: str
collected_slots: Dict[str, Any] = field(default_factory=dict)
current_slot: Optional[str] = None
context_data: Dict[str, Any] = field(default_factory=dict)
session_status: str = "active"
class SlotManager:
def __init__(self, db_connection):
self.db = db_connection
self.flows = {}
self.validators = {
ValidationType.REGEX: self._validate_regex,
ValidationType.LENGTH: self._validate_length,
ValidationType.RANGE: self._validate_range,
ValidationType.CHOICES: self._validate_choices,
}
def load_flow_configuration(self, flow_name: str) -> List[SlotDefinition]:
"""Load slot configuration from database"""
query = """
SELECT
sd.slot_name, sd.slot_type, sd.is_required, sd.priority,
sd.depends_on_slot, sd.dependency_value,
sv.validation_type, sv.validation_rule, sv.error_message,
sq.question_type, sq.question_text, sq.follow_up_text, sq.context_conditions
FROM conversation_flows cf
JOIN slot_definitions sd ON cf.id = sd.flow_id
LEFT JOIN slot_validations sv ON sd.id = sv.slot_id
LEFT JOIN slot_questions sq ON sd.id = sq.slot_id
WHERE cf.name = %s AND cf.is_active = true
ORDER BY sd.priority, sd.slot_name
"""
results = self.db.execute(query, (flow_name,)).fetchall()
# Group by slot_name
slots_data = {}
for row in results:
slot_name = row['slot_name']
if slot_name not in slots_data:
slots_data[slot_name] = {
'definition': {
'slot_name': row['slot_name'],
'slot_type': SlotType(row['slot_type']),
'is_required': row['is_required'],
'priority': row['priority'],
'depends_on_slot': row['depends_on_slot'],
'dependency_value': row['dependency_value'],
},
'validations': [],
'questions': []
}
# Add validation if present
if row['validation_type']:
validation = SlotValidation(
validation_type=ValidationType(row['validation_type']),
validation_rule=row['validation_rule'],
error_message=row['error_message']
)
slots_data[slot_name]['validations'].append(validation)
# Add question if present
if row['question_type']:
question = SlotQuestion(
question_type=QuestionType(row['question_type']),
question_text=row['question_text'],
follow_up_text=row['follow_up_text'],
context_conditions=row['context_conditions']
)
slots_data[slot_name]['questions'].append(question)
# Convert to SlotDefinition objects
slot_definitions = []
for slot_data in slots_data.values():
definition = SlotDefinition(
**slot_data['definition'],
validations=slot_data['validations'],
questions=slot_data['questions']
)
slot_definitions.append(definition)
return sorted(slot_definitions, key=lambda x: x.priority)
def get_next_slot_to_collect(self, session: ConversationSession,
slot_definitions: List[SlotDefinition]) -> Optional[SlotDefinition]:
"""Determine the next slot that needs to be collected"""
for slot_def in slot_definitions:
# Skip if already collected
if slot_def.slot_name in session.collected_slots:
continue
# Check if slot is required
if not slot_def.is_required:
continue
# Check dependencies
if slot_def.depends_on_slot:
if slot_def.depends_on_slot not in session.collected_slots:
continue # Dependency not met
collected_value = session.collected_slots[slot_def.depends_on_slot]
if slot_def.dependency_value and collected_value not in slot_def.dependency_value:
continue # Dependency condition not met
return slot_def
return None
def validate_slot_value(self, slot_def: SlotDefinition,
user_input: str) -> tuple[bool, Any, List[str]]:
"""Validate user input for a slot"""
errors = []
extracted_value = user_input.strip()
# Type-specific extraction
if slot_def.slot_type == SlotType.NUMBER:
try:
extracted_value = float(extracted_value)
except ValueError:
errors.append("Please enter a valid number")
return False, user_input, errors
elif slot_def.slot_type == SlotType.BOOLEAN:
lower_input = extracted_value.lower()
if lower_input in ['yes', 'y', 'true', '1']:
extracted_value = True
elif lower_input in ['no', 'n', 'false', '0']:
extracted_value = False
else:
errors.append("Please answer with yes or no")
return False, user_input, errors
# Run validations
for validation in slot_def.validations:
is_valid, error_msg = self._run_validation(validation, extracted_value)
if not is_valid:
errors.append(error_msg)
return len(errors) == 0, extracted_value, errors
def _run_validation(self, validation: SlotValidation, value: Any) -> tuple[bool, str]:
"""Run a specific validation rule"""
validator = self.validators.get(validation.validation_type)
if not validator:
return True, ""
try:
is_valid = validator(value, validation.validation_rule)
return is_valid, validation.error_message if not is_valid else ""
except Exception as e:
return False, f"Validation error: {str(e)}"
def _validate_regex(self, value: str, rule: Dict[str, Any]) -> bool:
pattern = rule.get('pattern', '')
return bool(re.match(pattern, str(value)))
def _validate_length(self, value: str, rule: Dict[str, Any]) -> bool:
length = len(str(value))
min_len = rule.get('min', 0)
max_len = rule.get('max', float('inf'))
return min_len <= length <= max_len
def _validate_range(self, value: float, rule: Dict[str, Any]) -> bool:
min_val = rule.get('min', float('-inf'))
max_val = rule.get('max', float('inf'))
return min_val <= value <= max_val
def _validate_choices(self, value: Any, rule: Dict[str, Any]) -> bool:
choices = rule.get('choices', [])
return value in choices
def get_question_for_slot(self, slot_def: SlotDefinition,
question_type: QuestionType = QuestionType.INITIAL,
context: Optional[Dict[str, Any]] = None) -> str:
"""Get appropriate question text for a slot"""
# Find matching question
for question in slot_def.questions:
if question.question_type == question_type:
# Check context conditions if present
if question.context_conditions and context:
if not self._check_context_conditions(question.context_conditions, context):
continue
question_text = question.question_text
if question.follow_up_text:
question_text += f" {question.follow_up_text}"
return question_text
# Fallback to generic question
return f"Could you please provide your {slot_def.slot_name.replace('_', ' ')}?"
def _check_context_conditions(self, conditions: Dict[str, Any],
context: Dict[str, Any]) -> bool:
"""Check if context conditions are met"""
for key, expected_value in conditions.items():
if key not in context:
return False
if context[key] != expected_value:
return False
return True
def save_conversation_session(self, session: ConversationSession):
"""Save session state to database"""
query = """
INSERT INTO conversation_sessions
(id, user_id, flow_id, session_status, current_slot, collected_slots, context_data)
VALUES (%s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO UPDATE SET
session_status = EXCLUDED.session_status,
current_slot = EXCLUDED.current_slot,
collected_slots = EXCLUDED.collected_slots,
context_data = EXCLUDED.context_data,
last_interaction = CURRENT_TIMESTAMP
"""
self.db.execute(query, (
session.session_id,
session.user_id,
session.flow_id,
session.session_status,
session.current_slot,
json.dumps(session.collected_slots),
json.dumps(session.context_data)
))
def is_conversation_complete(self, session: ConversationSession,
slot_definitions: List[SlotDefinition]) -> bool:
"""Check if all required slots have been collected"""
for slot_def in slot_definitions:
if slot_def.is_required and slot_def.slot_name not in session.collected_slots:
# Check if this slot should be skipped due to dependencies
if slot_def.depends_on_slot:
if slot_def.depends_on_slot not in session.collected_slots:
continue
collected_value = session.collected_slots[slot_def.depends_on_slot]
if slot_def.dependency_value and collected_value not in slot_def.dependency_value:
continue
return False
return True
# Example usage
def example_conversation_flow():
# This would be your actual database connection
db_connection = None # Replace with real DB connection
slot_manager = SlotManager(db_connection)
# Load flow configuration
flow_name = "customer_onboarding"
slot_definitions = slot_manager.load_flow_configuration(flow_name)
# Create new session
session = ConversationSession(
session_id=str(uuid.uuid4()),
user_id="user123",
flow_id=flow_name
)
# Main conversation loop
while not slot_manager.is_conversation_complete(session, slot_definitions):
# Get next slot to collect
next_slot = slot_manager.get_next_slot_to_collect(session, slot_definitions)
if not next_slot:
break
# Get question for this slot
question = slot_manager.get_question_for_slot(next_slot)
print(f"Bot: {question}")
# Simulate user input
user_input = input("User: ")
# Validate input
is_valid, extracted_value, errors = slot_manager.validate_slot_value(next_slot, user_input)
if is_valid:
session.collected_slots[next_slot.slot_name] = extracted_value
print(f"Bot: Great! I've got your {next_slot.slot_name}.")
else:
error_question = slot_manager.get_question_for_slot(
next_slot, QuestionType.VALIDATION_ERROR
)
print(f"Bot: {error_question}")
print(f"Errors: {', '.join(errors)}")
# Save session
slot_manager.save_conversation_session(session)
print("Bot: Thanks! I have all the information I need.")
session.session_status = "completed"
slot_manager.save_conversation_session(session)