Add functions to build Ollama messages and update chat handling logic
This commit is contained in:
143
backend/main.py
143
backend/main.py
@@ -493,6 +493,64 @@ def _row_to_history_message(row: models.ChatMessage) -> dict:
|
|||||||
payload["attachments"] = [_attachment_history_payload(attachment) for attachment in attachments]
|
payload["attachments"] = [_attachment_history_payload(attachment) for attachment in attachments]
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
async def _build_ollama_user_message(
|
||||||
|
message: str,
|
||||||
|
attachments: List[dict],
|
||||||
|
*,
|
||||||
|
enriched_message: Optional[str],
|
||||||
|
request_model_supports_vision: bool,
|
||||||
|
vision_model: Optional[str],
|
||||||
|
transcription_model: Optional[str],
|
||||||
|
persist_file_text: bool,
|
||||||
|
) -> tuple[dict, List[dict]]:
|
||||||
|
prepared_attachments, ollama_images, attachment_block = await _prepare_chat_message_attachments(
|
||||||
|
attachments,
|
||||||
|
request_model_supports_vision=request_model_supports_vision,
|
||||||
|
vision_model=vision_model,
|
||||||
|
transcription_model=transcription_model,
|
||||||
|
persist_file_text=persist_file_text,
|
||||||
|
)
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"role": "user",
|
||||||
|
"content": _compose_user_message_content(message, enriched_message, attachment_block),
|
||||||
|
}
|
||||||
|
if ollama_images:
|
||||||
|
payload["images"] = ollama_images
|
||||||
|
return payload, prepared_attachments
|
||||||
|
|
||||||
|
|
||||||
|
async def _build_ollama_messages_from_rows(
|
||||||
|
rows: List[models.ChatMessage],
|
||||||
|
*,
|
||||||
|
request_model_supports_vision: bool,
|
||||||
|
vision_model: Optional[str],
|
||||||
|
transcription_model: Optional[str],
|
||||||
|
override_user_row_index: Optional[int] = None,
|
||||||
|
override_user_content: Optional[str] = None,
|
||||||
|
) -> List[dict]:
|
||||||
|
conversation: List[dict] = []
|
||||||
|
for row_index, row in enumerate(rows):
|
||||||
|
if row.role != "user":
|
||||||
|
conversation.append({"role": row.role, "content": row.content})
|
||||||
|
continue
|
||||||
|
|
||||||
|
attachments = _load_message_attachments(getattr(row, "attachments_json", None), include_text=True)
|
||||||
|
enriched_message = override_user_content if override_user_row_index == row_index else None
|
||||||
|
message_payload, _prepared = await _build_ollama_user_message(
|
||||||
|
row.content,
|
||||||
|
attachments,
|
||||||
|
enriched_message=enriched_message,
|
||||||
|
request_model_supports_vision=request_model_supports_vision,
|
||||||
|
vision_model=vision_model,
|
||||||
|
transcription_model=transcription_model,
|
||||||
|
persist_file_text=False,
|
||||||
|
)
|
||||||
|
conversation.append(message_payload)
|
||||||
|
|
||||||
|
return conversation
|
||||||
|
|
||||||
def get_db():
|
def get_db():
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
@@ -659,8 +717,31 @@ async def chat(req: schemas.ChatRequest, db: Session = Depends(get_db)):
|
|||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(session)
|
db.refresh(session)
|
||||||
|
|
||||||
# Store the BASE user prompt
|
request_model_supports_vision = await _model_supports_vision(req.model)
|
||||||
user_attachments = _normalize_image_attachments(req.attachments)
|
prior_rows = (
|
||||||
|
db.query(models.ChatMessage)
|
||||||
|
.filter(models.ChatMessage.session_pk == session.id)
|
||||||
|
.order_by(models.ChatMessage.created_at.asc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
history_rows = prior_rows[-19:]
|
||||||
|
history_messages = await _build_ollama_messages_from_rows(
|
||||||
|
history_rows,
|
||||||
|
request_model_supports_vision=request_model_supports_vision,
|
||||||
|
vision_model=req.vision_model,
|
||||||
|
transcription_model=req.transcription_model,
|
||||||
|
)
|
||||||
|
|
||||||
|
current_user_message, user_attachments = await _build_ollama_user_message(
|
||||||
|
req.message,
|
||||||
|
req.attachments or [],
|
||||||
|
enriched_message=req.enriched_message,
|
||||||
|
request_model_supports_vision=request_model_supports_vision,
|
||||||
|
vision_model=req.vision_model,
|
||||||
|
transcription_model=req.transcription_model,
|
||||||
|
persist_file_text=True,
|
||||||
|
)
|
||||||
|
|
||||||
user_row = models.ChatMessage(
|
user_row = models.ChatMessage(
|
||||||
session_pk=session.id,
|
session_pk=session.id,
|
||||||
role='user',
|
role='user',
|
||||||
@@ -669,23 +750,7 @@ async def chat(req: schemas.ChatRequest, db: Session = Depends(get_db)):
|
|||||||
)
|
)
|
||||||
db.add(user_row)
|
db.add(user_row)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
messages = [*history_messages, current_user_message]
|
||||||
# Build minimal context (last 20)
|
|
||||||
last_msgs = (
|
|
||||||
db.query(models.ChatMessage)
|
|
||||||
.filter(models.ChatMessage.session_pk == session.id)
|
|
||||||
.order_by(models.ChatMessage.created_at.asc())
|
|
||||||
.all()[-20:]
|
|
||||||
)
|
|
||||||
messages = [_row_to_ollama_message(m) for m in last_msgs]
|
|
||||||
|
|
||||||
# Patch last user with enriched_message only for LLM call
|
|
||||||
if req.enriched_message:
|
|
||||||
for i in range(len(messages) - 1, -1, -1):
|
|
||||||
if messages[i].get("role") == "user":
|
|
||||||
messages = messages.copy()
|
|
||||||
messages[i] = {**messages[i], "content": req.enriched_message}
|
|
||||||
break
|
|
||||||
|
|
||||||
# Sources to persist with the assistant reply
|
# Sources to persist with the assistant reply
|
||||||
sources = req.sources or []
|
sources = req.sources or []
|
||||||
@@ -701,12 +766,19 @@ async def chat(req: schemas.ChatRequest, db: Session = Depends(get_db)):
|
|||||||
yield f"Ollama error: {e}"
|
yield f"Ollama error: {e}"
|
||||||
|
|
||||||
# Persist assistant reply (include sources_json)
|
# Persist assistant reply (include sources_json)
|
||||||
as_row = models.ChatMessage(
|
db_sess = None
|
||||||
session_pk=session.id, role='assistant', content=full_reply,
|
try:
|
||||||
sources_json=json.dumps(sources or [])
|
db_sess = SessionLocal()
|
||||||
)
|
db_sess.add(models.ChatMessage(
|
||||||
db.add(as_row)
|
session_pk=session.id,
|
||||||
db.commit()
|
role='assistant',
|
||||||
|
content=full_reply,
|
||||||
|
sources_json=json.dumps(sources or []),
|
||||||
|
))
|
||||||
|
db_sess.commit()
|
||||||
|
finally:
|
||||||
|
if db_sess is not None:
|
||||||
|
db_sess.close()
|
||||||
|
|
||||||
return StreamingResponse(stream_generator(), media_type="text/plain")
|
return StreamingResponse(stream_generator(), media_type="text/plain")
|
||||||
else:
|
else:
|
||||||
@@ -828,21 +900,22 @@ async def regenerate(session_id: str, req: schemas.RegenerateRequest, db: Sessio
|
|||||||
last_user_idx = i
|
last_user_idx = i
|
||||||
break
|
break
|
||||||
|
|
||||||
# prune after that user
|
request_model_supports_vision = await _model_supports_vision(model)
|
||||||
|
conversation = await _build_ollama_messages_from_rows(
|
||||||
|
msgs[: last_user_idx + 1],
|
||||||
|
request_model_supports_vision=request_model_supports_vision,
|
||||||
|
vision_model=req.vision_model,
|
||||||
|
transcription_model=req.transcription_model,
|
||||||
|
override_user_row_index=last_user_idx,
|
||||||
|
override_user_content=req.enriched_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
# prune after that user only after conversation building succeeds
|
||||||
if last_user_idx < len(msgs) - 1:
|
if last_user_idx < len(msgs) - 1:
|
||||||
for m in msgs[last_user_idx + 1:]:
|
for m in msgs[last_user_idx + 1:]:
|
||||||
db.delete(m)
|
db.delete(m)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
conversation = [_row_to_ollama_message(m) for m in msgs[: last_user_idx + 1]]
|
|
||||||
|
|
||||||
if req.enriched_message:
|
|
||||||
for j in range(len(conversation) - 1, -1, -1):
|
|
||||||
if conversation[j].get("role") == "user":
|
|
||||||
conversation = conversation.copy()
|
|
||||||
conversation[j] = {**conversation[j], "content": req.enriched_message}
|
|
||||||
break
|
|
||||||
|
|
||||||
session_pk = session.id
|
session_pk = session.id
|
||||||
|
|
||||||
if stream:
|
if stream:
|
||||||
|
|||||||
Reference in New Issue
Block a user