fix: improve maintenance and synchronize live conversations
This commit is contained in:
1 parent
bb541bd212
commit
ef86a5d3af
297 files changed
+2983
-1274
No files matched your search
@@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from types import SimpleNamespace
|
||||
from fastapi import APIRouter
|
||||
@@ -50,7 +47,11 @@ from app.services.file_storage import resolve_public_url
|
||||
from app.models.session_to_agent import ACTIVE_STATUSES, SessionStatus
|
||||
from app.agents.transfer_agent import get_agent_availability_response
|
||||
from app.repositories.agent import AgentRepository
|
||||
from app.services.agent_rooms import is_org_inbox_room, may_join_org_inbox
|
||||
from app.services.agent_rooms import (
|
||||
CHAT_WATCH_PREFIX, ORG_CHAT_UPDATES_PREFIX, is_org_inbox_room,
|
||||
may_join_chat_updates, may_join_org_inbox, may_watch_chat,
|
||||
notify_conversation_changed,
|
||||
)
|
||||
from app.services.chat_notifications import notify_new_chat
|
||||
from app.services.contact_capture import retain_unstored_email
|
||||
from app.services.human_routing import (
|
||||
@@ -637,6 +638,7 @@ async def handle_widget_chat(sid, data):
|
||||
)
|
||||
db.add(file_attachment)
|
||||
db.commit()
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
chat_history = []
|
||||
chat_history = await chat_repo.get_session_history(session_id)
|
||||
jira_repo = JiraRepository(db)
|
||||
@@ -691,6 +693,7 @@ async def handle_widget_chat(sid, data):
|
||||
"shopify_output": response_content.shopify_output
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
response = response_content
|
||||
elif active_session.workflow_id and active_session.user_id is not None:
|
||||
# Workflow session but human agent has taken over - handle like regular human takeover
|
||||
@@ -720,6 +723,7 @@ async def handle_widget_chat(sid, data):
|
||||
)
|
||||
db.add(file_attachment)
|
||||
db.commit()
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
# Get session data to find assigned user
|
||||
session_data = session_repo.get_session(session_id)
|
||||
user_id = str(session_data.user_id) if session_data and session_data.user_id else None
|
||||
@@ -779,6 +783,7 @@ async def handle_widget_chat(sid, data):
|
||||
)
|
||||
db.add(file_attachment)
|
||||
db.commit()
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
# Get session data to find assigned user
|
||||
session_data = session_repo.get_session(session_id)
|
||||
user_id = str(session_data.user_id) if session_data and session_data.user_id else None
|
||||
@@ -1291,6 +1296,7 @@ async def handle_agent_message(sid, data):
|
||||
logger.info(f"Agent ended chat session {session_id}")
|
||||
# Update session status to closed
|
||||
session_repo.update_session_status(session_id, "CLOSED")
|
||||
await notify_conversation_changed(session.get('organization_id'), session_id)
|
||||
|
||||
|
||||
# Emit to widget clients
|
||||
@@ -1396,6 +1402,22 @@ async def handle_join_room(sid, data):
|
||||
await sio.enter_room(sid, session_id, namespace='/agent')
|
||||
return
|
||||
|
||||
# These rooms carry invalidations only, never message content. A
|
||||
# watcher must satisfy the same scope as the chat-detail endpoint.
|
||||
if session_id.startswith(ORG_CHAT_UPDATES_PREFIX):
|
||||
db = next(get_db())
|
||||
if not may_join_chat_updates(db, session, session_id):
|
||||
raise ValueError("Unauthorized to join chat updates room")
|
||||
await sio.enter_room(sid, session_id, namespace='/agent')
|
||||
return
|
||||
|
||||
if session_id.startswith(CHAT_WATCH_PREFIX):
|
||||
db = next(get_db())
|
||||
if not await may_watch_chat(db, session, session_id):
|
||||
raise ValueError("Unauthorized to watch chat")
|
||||
await sio.enter_room(sid, session_id, namespace='/agent')
|
||||
return
|
||||
|
||||
# Verify this agent has permission to join this room
|
||||
db = next(get_db())
|
||||
session_repo = SessionToAgentRepository(db)
|
||||
@@ -1451,7 +1473,9 @@ async def handle_leave_room(sid, data):
|
||||
|
||||
# Notify a conversation room of the leave — not the org inbox room,
|
||||
# where the whole team would hear every chat switch
|
||||
if not is_org_inbox_room(session_id):
|
||||
if not (is_org_inbox_room(session_id) or session_id.startswith(
|
||||
(ORG_CHAT_UPDATES_PREFIX, CHAT_WATCH_PREFIX)
|
||||
)):
|
||||
await sio.emit('room_event', {
|
||||
'type': 'leave',
|
||||
'user_id': str(session.get('user_id')),
|
||||
@@ -1641,6 +1665,7 @@ async def handle_get_workflow_state(sid):
|
||||
"initial_execution": True
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
|
||||
# Emit intermediate message to client immediately
|
||||
await sio.emit('chat_response', {
|
||||
@@ -1686,6 +1711,7 @@ async def handle_get_workflow_state(sid):
|
||||
"initial_message": True
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
|
||||
# The widget does not render a workflow_state message, so
|
||||
# send the text as a chat_response like the other handlers.
|
||||
@@ -1934,6 +1960,7 @@ async def handle_proceed_workflow(sid, data):
|
||||
"message_node": True
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
|
||||
# Emit intermediate message to client immediately
|
||||
await sio.emit('chat_response', {
|
||||
@@ -1971,6 +1998,7 @@ async def handle_proceed_workflow(sid, data):
|
||||
"request_rating": workflow_result.request_rating,
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
|
||||
# Emit chat response
|
||||
await sio.emit('chat_response', {
|
||||
@@ -2109,6 +2137,7 @@ async def handle_form_submission(sid, data):
|
||||
# Validate form data
|
||||
form_data = data.get('form_data', {})
|
||||
if not form_data:
|
||||
logger.warning(f"Form submission rejected for sid {sid}: no form data")
|
||||
raise ValueError("No form data provided")
|
||||
|
||||
db = next(get_db())
|
||||
@@ -2117,6 +2146,7 @@ async def handle_form_submission(sid, data):
|
||||
active_session = _own_session(session_repo, session)
|
||||
|
||||
if not active_session or not active_session.workflow_id:
|
||||
logger.warning(f"Form submission rejected for sid {sid}: no active workflow session")
|
||||
raise ValueError("No active workflow session found")
|
||||
|
||||
# Get the current form configuration for validation
|
||||
@@ -2125,6 +2155,7 @@ async def handle_form_submission(sid, data):
|
||||
workflow = workflow_repo.get_workflow_with_nodes_and_connections(active_session.workflow_id)
|
||||
|
||||
if not workflow:
|
||||
logger.warning(f"Form submission rejected for sid {sid}: workflow not found")
|
||||
raise ValueError("Workflow not found")
|
||||
|
||||
# Find the current form node to get field configurations
|
||||
@@ -2144,7 +2175,7 @@ async def handle_form_submission(sid, data):
|
||||
|
||||
if validation_errors:
|
||||
error_message = "; ".join(validation_errors)
|
||||
logger.error(f"Form validation failed: {error_message}")
|
||||
logger.error(f"Form validation failed for sid {sid}: {error_message}")
|
||||
await sio.emit('error', {
|
||||
'error': error_message,
|
||||
'type': 'validation_error'
|
||||
@@ -2184,14 +2215,13 @@ async def handle_form_submission(sid, data):
|
||||
existing_customer = customer_repo.get_customer_by_email(submitted_email, org_id)
|
||||
if not existing_customer:
|
||||
# Update customer email
|
||||
old_email = customer.email
|
||||
customer.email = submitted_email
|
||||
db.commit()
|
||||
logger.info(f"Updated customer {customer_id} email from {old_email} to {submitted_email}")
|
||||
logger.info(f"Updated email for customer {customer_id} in session {session_id}")
|
||||
else:
|
||||
logger.warning(f"Email {submitted_email} already exists for another customer, skipping update")
|
||||
logger.warning(f"Email already belongs to customer {existing_customer.id}; skipping update for customer {customer_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update customer email: {str(e)}")
|
||||
logger.error(f"Failed to update email for customer {customer_id}: {type(e).__name__}")
|
||||
db.rollback()
|
||||
|
||||
# Store form submission in chat history
|
||||
@@ -2201,8 +2231,6 @@ async def handle_form_submission(sid, data):
|
||||
if workflow_result.intermediate_messages:
|
||||
logger.info(f"Found {len(workflow_result.intermediate_messages)} intermediate messages from MESSAGE nodes")
|
||||
for intermediate_message in workflow_result.intermediate_messages:
|
||||
logger.debug(f"Emitting intermediate message: {intermediate_message}")
|
||||
|
||||
# Store intermediate message in chat history
|
||||
chat_repo.create_message({
|
||||
"message": intermediate_message,
|
||||
@@ -2218,6 +2246,7 @@ async def handle_form_submission(sid, data):
|
||||
"message_node": True
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
|
||||
# Emit intermediate message to client immediately
|
||||
await sio.emit('chat_response', {
|
||||
@@ -2248,6 +2277,7 @@ async def handle_form_submission(sid, data):
|
||||
"request_rating": workflow_result.request_rating,
|
||||
}
|
||||
})
|
||||
await notify_conversation_changed(org_id, session_id)
|
||||
|
||||
# Emit chat response
|
||||
await sio.emit('chat_response', {
|
||||
@@ -2273,14 +2303,13 @@ async def handle_form_submission(sid, data):
|
||||
}, to=sid, namespace='/widget')
|
||||
|
||||
except ValueError as e:
|
||||
logger.error(f"Form submission error for sid {sid}: {str(e)}")
|
||||
logger.error(f"Form submission error for sid {sid}: {type(e).__name__}")
|
||||
await sio.emit('error', {
|
||||
'error': str(e),
|
||||
'type': 'form_error'
|
||||
}, to=sid, namespace='/widget')
|
||||
except Exception as e:
|
||||
logger.error(f"Form submission error for sid {sid}: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error(f"Form submission error for sid {sid}: {type(e).__name__}")
|
||||
await sio.emit('error', {
|
||||
'error': 'Failed to submit form',
|
||||
'type': 'form_error'
|
||||
|
||||
Reference in new issue
Block a user