feat: Langfuse session grouping for multi-turn chat traces (#15679)

## Summary

This PR passes `session_id` into Langfuse trace observations so
multi-turn chat messages can be grouped under the same session in
Langfuse.

Changes include:
- Propagate `session_id` from chat/session APIs into
`dialog_service.async_chat`.
- Pass `session_id` into Langfuse `start_observation(...)`.
- Share Langfuse `trace_context` with chat, embedding, rerank, and TTS
model bundles where applicable.
- Add unit coverage to verify Langfuse observations receive
`session_id`.
- Update affected test stubs for the new optional Langfuse context
arguments.

## Related Issue
Closes: #15636 

## Change Type
- [x] Feature
- [x] Bug fix
- [x] Test
- [ ] Refactor
- [ ] Documentation
- [ ] Breaking change

## Real Behavior Proof

Before this change:

- Langfuse observations were created without `session_id`.
- Multi-turn chat traces could not be grouped by session in Langfuse.

After this change:

- Chat/session flows pass `session_id` into `async_chat`.
- Langfuse observations include `session_id`.
- Related model bundles receive shared trace context and session
metadata.

Validation result:

```bash
uv run python -m py_compile \
  api/db/services/tenant_llm_service.py \
  api/db/services/llm_service.py \
  api/db/services/dialog_service.py \
  api/db/services/conversation_service.py \
  api/apps/restful_apis/chat_api.py \
  test/unit_test/api/db/services/test_dialog_service_final_answer.py \
  test/unit_test/api/db/services/test_dialog_service_use_sql_source_columns.py
```
Passed.

```bash
uv run pytest \
  test/unit_test/api/db/services/test_dialog_service_final_answer.py \
  test/unit_test/api/db/services/test_dialog_service_use_sql_source_columns.py -q
```
Result:

```text
11 passed in 16.89s
```

```bash
git diff --check
```
Passed.
## Checklist

- [x] Analyzed the issue requirement.
- [x] Checked existing Langfuse trace integration.
- [x] Implemented only the requested session grouping behavior.
- [x] Added/updated unit tests.
- [x] Ran focused tests successfully.
- [x] Ran Python compile validation.
- [x] Ran whitespace diff validation.
This commit is contained in:
Jonathan Chang
2026-06-12 09:18:06 +07:00
committed by GitHub
parent 0d836afd34
commit de06c9a60b
7 changed files with 153 additions and 42 deletions

View File

@@ -1242,7 +1242,7 @@ async def session_completion(chat_id_in_arg=""):
"""Yield SSE-formatted chunks from the async chat generator."""
nonlocal dia, msg, req, conv
try:
async for ans in async_chat(dia, msg, True, **req):
async for ans in async_chat(dia, msg, True, session_id=session_id, **req):
ans = _format_answer(ans)
payload = _sanitize_json_floats({"code": 0, "message": "", "data": ans})
yield "data:" + json.dumps(payload, ensure_ascii=False) + "\n\n"
@@ -1262,7 +1262,7 @@ async def session_completion(chat_id_in_arg=""):
return resp
answer = None
async for ans in async_chat(dia, msg, False, **req):
async for ans in async_chat(dia, msg, False, session_id=session_id, **req):
answer = _format_answer(ans)
if conv is not None:
await thread_pool_exec(ConversationService.update_by_id, conv.id, conv.to_dict())