mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-05 06:41:13 +02:00
docs: update AI chat API design with detailed statistics collection
Update the AI chat API design documentation to provide comprehensive details about statistics collection during streaming conversations. The documentation now includes: 1. **Enhanced statistics collection logic**: - Clarified message_count increments for user messages, AI responses, and tool results - Added LLM call tracking via on_chat_model_start events - Detailed token counting methodology using LangGraph's usage_metadata 2. **Improved implementation details**: - Added specific event handlers for statistics collection - Explained LangGraph's cumulative token counting behavior - Provided real-world examples of token accumulation 3. **Updated data models**: - Enhanced ChatSession model documentation with field descriptions - Separated fields into categories (basic, statistics, timestamps, reserved) 4. **Refined architecture documentation**: - Added detailed flow for stream_chat method - Documented statistics collection mechanism during SSE streaming - Explained batch update strategy to reduce database writes The changes ensure developers understand how conversation statistics are collected, processed, and stored without impacting streaming performance.
This commit is contained in:
1 parent
48e2793719
commit
6b73f00281
2 files changed
+158
-52
No files matched your search
@@ -233,30 +233,72 @@ class AgentService:
|
||||
# Track statistics for session update
|
||||
message_count = 1 # User message
|
||||
llm_calls_count = 0
|
||||
tool_calls_count = 0
|
||||
input_tokens = 0
|
||||
output_tokens = 0
|
||||
last_message_at = datetime.utcnow().isoformat()
|
||||
|
||||
# Track if we've counted the AI response for this turn
|
||||
ai_response_counted = False
|
||||
tool_messages_counted = 0
|
||||
|
||||
# Stream events
|
||||
try:
|
||||
async for event in graph.astream_events(inputs, config=config, version="v2"):
|
||||
event_type = event.get("event", "")
|
||||
data = event.get("data", {})
|
||||
|
||||
# Track LLM calls and tokens
|
||||
if event_type == "on_chat_model_start":
|
||||
llm_calls_count += 1
|
||||
log.debug("LLM call started, count=%d", llm_calls_count)
|
||||
|
||||
elif event_type == "on_chat_model_end":
|
||||
# Extract token usage from response metadata
|
||||
# Try multiple possible locations where token usage might be stored
|
||||
token_info_found = False
|
||||
|
||||
# Method 1: response.usage_metadata
|
||||
response = data.get("response", {})
|
||||
if hasattr(response, "usage_metadata"):
|
||||
usage = response.usage_metadata
|
||||
if usage:
|
||||
input_tokens += usage.get("input_tokens", 0)
|
||||
output_tokens += usage.get("output_tokens", 0)
|
||||
token_info_found = True
|
||||
|
||||
# Method 2: output.usage_metadata
|
||||
if not token_info_found:
|
||||
output_msg = data.get("output", {})
|
||||
if hasattr(output_msg, "usage_metadata"):
|
||||
usage = output_msg.usage_metadata
|
||||
if usage:
|
||||
input_tokens += usage.get("input_tokens", 0)
|
||||
output_tokens += usage.get("output_tokens", 0)
|
||||
token_info_found = True
|
||||
|
||||
# Method 3: Check data directly for token usage fields
|
||||
if not token_info_found:
|
||||
if "input_tokens" in data:
|
||||
input_tokens += data.get("input_tokens", 0)
|
||||
if "output_tokens" in data:
|
||||
output_tokens += data.get("output_tokens", 0)
|
||||
if "input_tokens" in data or "output_tokens" in data:
|
||||
token_info_found = True
|
||||
|
||||
# Count AI response as one message (only once per turn)
|
||||
if not ai_response_counted:
|
||||
message_count += 1
|
||||
ai_response_counted = True
|
||||
|
||||
# Track tool messages
|
||||
elif event_type == "on_tool_end":
|
||||
message_count += 1 # Tool result message
|
||||
tool_messages_counted += 1
|
||||
log.debug("Tool message counted, message_count=%d", message_count)
|
||||
|
||||
# Convert event to chunk for SSE streaming
|
||||
chunk = self._convert_event_to_chunk(event, session_id)
|
||||
if chunk:
|
||||
# Track statistics
|
||||
if chunk.get("type") == "content":
|
||||
message_count += 1 # AI response
|
||||
elif chunk.get("type") == "tool_start":
|
||||
tool_calls_count += 1
|
||||
elif chunk.get("type") == "tool_end":
|
||||
message_count += 1 # Tool message
|
||||
|
||||
# Track tokens if available
|
||||
if chunk.get("type") == "content" and "input_tokens" in chunk:
|
||||
input_tokens += chunk.get("input_tokens", 0)
|
||||
if chunk.get("type") == "content" and "output_tokens" in chunk:
|
||||
output_tokens += chunk.get("output_tokens", 0)
|
||||
|
||||
log.debug("Yielding chunk: type=%s", chunk.get("type"))
|
||||
yield chunk
|
||||
|
||||
@@ -270,8 +312,8 @@ class AgentService:
|
||||
total_tokens=input_tokens + output_tokens,
|
||||
last_message_at=last_message_at
|
||||
)
|
||||
log.debug("Updated session statistics: thread_id=%s, messages=%d, tokens=%d",
|
||||
session_id, message_count, input_tokens + output_tokens)
|
||||
log.info("Session statistics updated: thread_id=%s, messages=%d, llm_calls=%d, tokens=%d+%d=%d",
|
||||
session_id, message_count, llm_calls_count, input_tokens, output_tokens, input_tokens + output_tokens)
|
||||
|
||||
# Sync auto-generated title from checkpoint state
|
||||
final_state = await graph.aget_state(config)
|
||||
|
||||
Reference in new issue
Block a user