kboss/b/cntoai/chat_send.dspy
2026-05-22 19:18:37 +08:00

161 lines
5.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

def _escape(value):
if value is None:
return None
return str(value).replace("'", "''")
def _title_from_message(ns):
text = ns.get('message') or ns.get('text') or ''
text = str(text).strip().replace('\n', ' ')
if not text:
return '新对话'
return text[:30] + ('...' if len(text) > 30 else '')
def _build_user_content(ns):
text_parts = []
if ns.get('message'):
text_parts.append(str(ns.get('message')))
if ns.get('text'):
text_parts.append(str(ns.get('text')))
if ns.get('document_text'):
text_parts.append(str(ns.get('document_text')))
parts = []
merged_text = '\n'.join([p for p in text_parts if p]).strip()
if merged_text:
parts.append({'type': 'text', 'text': merged_text})
if ns.get('image_url'):
parts.append({'type': 'image_url', 'image_url': {'url': ns.get('image_url')}})
if ns.get('image_base64'):
mime = ns.get('image_mime') or 'image/jpeg'
b64 = ns.get('image_base64')
if not str(b64).startswith('data:'):
b64 = 'data:%s;base64,%s' % (mime, b64)
parts.append({'type': 'image_url', 'image_url': {'url': b64}})
if ns.get('document_url'):
parts.append({'type': 'file', 'file': {'file_url': ns.get('document_url')}})
if not parts:
return ''
if len(parts) == 1 and parts[0]['type'] == 'text':
return parts[0]['text']
return parts
async def _load_session_messages(sor, session_id):
sql = """
SELECT role, content, content_type
FROM chat_message
WHERE session_id = '%s'
ORDER BY created_at ASC;
""" % _escape(session_id)
rows = await sor.sqlExe(sql, {})
messages = []
for row in rows:
content = row.get('content') or ''
if row.get('content_type') == 'mixed':
import json
try:
content = json.loads(content)
except Exception:
pass
messages.append({'role': row['role'], 'content': content})
return messages
async def chat_send(ns={}):
"""
发送消息并保存多轮对话(需先执行 chat_tables.sql
参数model, message, stream(默认true), session_id,
image_url, image_base64, document_url, document_text
"""
import json
import traceback
model = ns.get('model')
if not model:
return {'status': False, 'msg': 'model is required'}
userid = ns.get('userid') or await get_user()
if not userid:
return {'status': False, 'msg': '未找到用户'}
user_content = _build_user_content(ns)
if not user_content:
return {'status': False, 'msg': '请输入文本,或提供图片/文档参数'}
content_type = 'mixed' if isinstance(user_content, list) else 'text'
store_content = json.dumps(user_content, ensure_ascii=False) if content_type == 'mixed' else str(user_content)
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
session_id = ns.get('session_id')
if not session_id:
session_id = uuid()
await sor.C('chat_session', {
'id': session_id,
'userid': userid,
'model': model,
'title': _title_from_message(ns),
})
else:
sessions = await sor.R('chat_session', {'id': session_id, 'userid': userid})
if not sessions:
return {'status': False, 'msg': '会话不存在'}
await sor.C('chat_message', {
'id': uuid(),
'session_id': session_id,
'role': 'user',
'content': store_content,
'content_type': content_type,
})
history = await _load_session_messages(sor, session_id)
stream_val = ns.get('stream', True)
if isinstance(stream_val, str):
stream_val = stream_val.lower() in ('1', 'true', 'yes', 'on')
chat_result = await path_call('llm_chat_completions.dspy', {
'model': model,
'messages': history,
'stream': stream_val,
'userid': userid,
'api_url': ns.get('api_url'),
'api_key': ns.get('api_key'),
'model_id': ns.get('model_id'),
})
if not chat_result.get('status'):
return chat_result
reply = chat_result['data']['reply']
await sor.C('chat_message', {
'id': uuid(),
'session_id': session_id,
'role': 'assistant',
'content': reply,
'content_type': 'text',
})
await sor.sqlExe(
"UPDATE chat_session SET updated_at = NOW() WHERE id = '%s';"
% _escape(session_id),
{},
)
return {
'status': True,
'msg': 'send success',
'data': {
'session_id': session_id,
'reply': reply,
'model': model,
},
}
except Exception:
return {'status': False, 'msg': 'send failed, %s' % traceback.format_exc()}
ret = await chat_send(params_kw)
return ret