68 lines
3.3 KiB
Python
68 lines
3.3 KiB
Python
"""Commit one model reply and its candidate state together, under ownership."""
|
|
|
|
import json
|
|
|
|
from .db import connect, make_title, now_str
|
|
|
|
|
|
def latest_message_id(conv_id):
|
|
if not conv_id:
|
|
return 0
|
|
connection = connect()
|
|
try:
|
|
return connection.execute('SELECT COALESCE(MAX(id),0) FROM messages WHERE conv_id=?', (conv_id,)).fetchone()[0]
|
|
finally:
|
|
connection.close()
|
|
|
|
|
|
def save_exchange(conv_id, question, answer, *, previous_message_id, quote_turn=None, list_state=None,
|
|
request_id=None, payload=None):
|
|
"""Return a message ID or a failure marker; never leave a partial exchange."""
|
|
if not conv_id:
|
|
return None
|
|
from flask import has_request_context
|
|
from .customer_service import attach_customer, current_customer_id, current_scope_key
|
|
from .visitor_service import visitor_id
|
|
from .conversation_state import commit_state
|
|
|
|
connection = connect()
|
|
try:
|
|
connection.execute('BEGIN IMMEDIATE')
|
|
if has_request_context():
|
|
# The write lock keeps session revocation from racing this commit.
|
|
attach_customer()
|
|
row = connection.execute('SELECT title FROM chat_history WHERE id=? AND visitor_id=? '
|
|
'AND (customer_id IS NULL OR (customer_id=? AND customer_scope=?))',
|
|
(conv_id, visitor_id(), current_customer_id(), current_scope_key())).fetchone()
|
|
if row is None:
|
|
return 'deleted'
|
|
from .chat_requests import existing_result, remember_result
|
|
cached = existing_result(connection, conv_id, request_id, question)
|
|
if cached is not None:
|
|
return cached
|
|
latest = connection.execute('SELECT id,role,content FROM messages WHERE conv_id=? ORDER BY id DESC LIMIT 1',
|
|
(conv_id,)).fetchone()
|
|
if (latest[0] if latest else 0) != previous_message_id:
|
|
return 'stale'
|
|
stamp = now_str()
|
|
if not (latest and latest[1] == 'user' and latest[2] == question):
|
|
connection.execute('INSERT INTO messages(conv_id,role,content,ctime) VALUES (?,?,?,?)',
|
|
(conv_id, 'user', question, stamp))
|
|
cursor = connection.execute('INSERT INTO messages(conv_id,role,content,ctime) VALUES (?,?,?,?)',
|
|
(conv_id, 'assistant', answer, stamp))
|
|
message_id = cursor.lastrowid
|
|
if quote_turn is not None and quote_turn.candidate is not None:
|
|
if not commit_state(connection, conv_id, quote_turn.candidate, quote_turn.expected_version):
|
|
return 'stale'
|
|
connection.execute('UPDATE chat_history SET utime=?,title=? WHERE id=?',
|
|
(stamp, make_title(question) if not row[0] or row[0] == '新对话' else row[0], conv_id))
|
|
if list_state is not None:
|
|
state = {**list_state, 'message_id': message_id}
|
|
connection.execute('UPDATE chat_history SET order_list_state=? WHERE id=? AND customer_id=? AND customer_scope=?',
|
|
(json.dumps(state, ensure_ascii=False), conv_id, current_customer_id(), current_scope_key()))
|
|
remember_result(connection, conv_id, request_id, question, payload, message_id, quote_turn)
|
|
connection.commit()
|
|
return message_id
|
|
finally:
|
|
connection.close()
|