## Summary - Fixes token tracking for [#16](#16): streaming chat never persisted `PromptMetric.tokens_in` / `tokens_out` (admin + account usage showed `—`). - Drop `StrOutputParser` on async LLM/RAG/data-analysis chains so Ollama `generation_info` (`prompt_eval_count` / `eval_count`) survives; collect usage while streaming via `TokenUsageCollector`. - Stop calling `self.close()` in `disconnect` (fixes Grafana `Unexpected ASGI message 'websocket.close'`). ## Test plan - [x] Unit tests: `test_utils`, consumers, LLM/RAG/data-analysis services, finance quotas - [ ] Deploy / local: send a chat prompt, confirm admin Prompt Metrics shows Tokens In/Out - [ ] Reload Account usage card — in/out no longer `—` for new turns - [ ] Confirm WS disconnect no longer raises double-close in logsReviewed-on: #38
This commit was merged in pull request #38.
This commit is contained in:
1 parent
841c0962d9
commit
cc45ae5808
7 files changed
+139
-22
No files matched your search
@@ -23,6 +23,8 @@ from .services.moderation_classifier import moderation_classifier, ModerationLab
|
||||
from .services.prompt_classifier.prompt_classifier import PromptClassifier, PromptType
|
||||
from .services.data_analysis_service import AsyncDataAnalysisService
|
||||
from .utils import (
|
||||
TokenUsageCollector,
|
||||
aiter_text_chunks,
|
||||
extract_token_usage,
|
||||
has_usable_user_prompt,
|
||||
is_heartbeat_payload,
|
||||
@@ -323,7 +325,9 @@ class ChatConsumerGraph(AsyncWebsocketConsumer):
|
||||
await self.accept()
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
await self.close()
|
||||
# Connection already closing — do not call self.close() again
|
||||
# (triggers ASGI 'websocket.close' after close completed).
|
||||
pass
|
||||
|
||||
async def send_json_message(self, data_str):
|
||||
try:
|
||||
@@ -453,24 +457,29 @@ class ChatConsumerGraph(AsyncWebsocketConsumer):
|
||||
await self.send("START_OF_THE_STREAM_ENDER_GAME_42")
|
||||
|
||||
full_response = ""
|
||||
|
||||
tokens_in = tokens_out = None
|
||||
|
||||
if isinstance(response_generator_or_dict, dict):
|
||||
content = response_generator_or_dict.get("content", "")
|
||||
await self.send_json_message(json.dumps(response_generator_or_dict))
|
||||
full_response = content
|
||||
tokens_in, tokens_out = extract_token_usage(
|
||||
response_generator_or_dict
|
||||
)
|
||||
else:
|
||||
async for chunk in response_generator_or_dict:
|
||||
# Stream raw LLM chunks so final Ollama generation_info
|
||||
# (prompt_eval_count / eval_count) is not stripped.
|
||||
usage = TokenUsageCollector()
|
||||
async for chunk in aiter_text_chunks(
|
||||
response_generator_or_dict, usage
|
||||
):
|
||||
full_response += chunk
|
||||
await self.send_json_message(chunk)
|
||||
tokens_in, tokens_out = usage.pair
|
||||
|
||||
await self.send("END_OF_THE_STREAM_ENDER_GAME_42")
|
||||
|
||||
await save_generated_message(conversation_id, full_response)
|
||||
tokens_in, tokens_out = extract_token_usage(
|
||||
response_generator_or_dict
|
||||
if isinstance(response_generator_or_dict, dict)
|
||||
else None
|
||||
)
|
||||
await finish_prompt_metric(
|
||||
prompt_metric,
|
||||
len(full_response),
|
||||
|
||||
Reference in new issue
Block a user