mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
* feat(wecom): add user feedback support for WeChat Work AI Bot This commit implements user feedback functionality (like/dislike) for WeChat Work AI Bot conversations, including: Backend changes: - Add feedback_id and stream_id fields to WecomBotEvent - Implement feedback event handling in WecomBotClient (api.py) - Add StreamSessionManager._feedback_index for feedback_id lookup - Add on_feedback decorator for custom feedback handlers - Create MonitoringFeedback entity for database persistence - Add dbm025 migration for monitoring_feedback table - Implement FeedbackMonitor helper class - Update all platform adapters with ap parameter support - Update botmgr to pass bot_info for monitoring context Frontend changes: - Add FeedbackCard and FeedbackList components - Add useFeedbackData hook for feedback data fetching - Add feedback tab to monitoring page - Add feedback types and interfaces - Add i18n translations (zh-Hans, en-US) Other changes: - Update Dockerfile with Chinese mirror for faster builds - Update docker-compose.yaml with network configuration - Update .gitignore for docker data and backup files Note: Known issues that need future improvement: - feedback_type=3 (cancel) is recorded but not properly handled - Duplicate feedback records are not deduplicated * chore: remove unnecessary migration for new table will be created automatically * chore: ruff format * chore: prettier * feat: add feedback handling support across multiple platform adapters * fix(web): remove unused imports and variables in monitoring module --------- Co-authored-by: 6mvp6 <13727783693@163.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com>
132 lines
7.6 KiB
Python
132 lines
7.6 KiB
Python
import sqlalchemy
|
|
|
|
from .base import Base
|
|
|
|
|
|
class MonitoringMessage(Base):
|
|
"""Monitoring message records"""
|
|
|
|
__tablename__ = 'monitoring_messages'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True)
|
|
timestamp = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
bot_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
bot_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
pipeline_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
pipeline_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
message_content = sqlalchemy.Column(sqlalchemy.Text, nullable=False)
|
|
session_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
status = sqlalchemy.Column(sqlalchemy.String(50), nullable=False) # success, error, pending
|
|
level = sqlalchemy.Column(sqlalchemy.String(50), nullable=False) # info, warning, error, debug
|
|
platform = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
user_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
user_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) # User display name
|
|
runner_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) # Runner name for this query
|
|
variables = sqlalchemy.Column(sqlalchemy.Text, nullable=True) # Query variables as JSON string
|
|
role = sqlalchemy.Column(sqlalchemy.String(50), nullable=True, default='user') # user, assistant
|
|
|
|
|
|
class MonitoringLLMCall(Base):
|
|
"""LLM call records"""
|
|
|
|
__tablename__ = 'monitoring_llm_calls'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True)
|
|
timestamp = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
model_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
input_tokens = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
|
|
output_tokens = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
|
|
total_tokens = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
|
|
duration = sqlalchemy.Column(sqlalchemy.Integer, nullable=False) # milliseconds
|
|
cost = sqlalchemy.Column(sqlalchemy.Float, nullable=True)
|
|
status = sqlalchemy.Column(sqlalchemy.String(50), nullable=False) # success, error
|
|
bot_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
bot_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
pipeline_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
pipeline_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
session_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
error_message = sqlalchemy.Column(sqlalchemy.Text, nullable=True)
|
|
message_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True) # Associated message ID
|
|
|
|
|
|
class MonitoringSession(Base):
|
|
"""Session tracking records"""
|
|
|
|
__tablename__ = 'monitoring_sessions'
|
|
|
|
session_id = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True)
|
|
bot_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
bot_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
pipeline_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
pipeline_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
message_count = sqlalchemy.Column(sqlalchemy.Integer, nullable=False, default=0)
|
|
start_time = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
last_activity = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
is_active = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=True, index=True)
|
|
platform = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
user_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
user_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) # User display name
|
|
|
|
|
|
class MonitoringError(Base):
|
|
"""Error log records"""
|
|
|
|
__tablename__ = 'monitoring_errors'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True)
|
|
timestamp = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
error_type = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
error_message = sqlalchemy.Column(sqlalchemy.Text, nullable=False)
|
|
bot_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
bot_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
pipeline_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, index=True)
|
|
pipeline_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
session_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
stack_trace = sqlalchemy.Column(sqlalchemy.Text, nullable=True)
|
|
message_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True) # Associated message ID
|
|
|
|
|
|
class MonitoringEmbeddingCall(Base):
|
|
"""Embedding call records"""
|
|
|
|
__tablename__ = 'monitoring_embedding_calls'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True)
|
|
timestamp = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
model_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=False)
|
|
prompt_tokens = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
|
|
total_tokens = sqlalchemy.Column(sqlalchemy.Integer, nullable=False)
|
|
duration = sqlalchemy.Column(sqlalchemy.Integer, nullable=False) # milliseconds
|
|
input_count = sqlalchemy.Column(sqlalchemy.Integer, nullable=False) # Number of input texts
|
|
status = sqlalchemy.Column(sqlalchemy.String(50), nullable=False) # success, error
|
|
error_message = sqlalchemy.Column(sqlalchemy.Text, nullable=True)
|
|
# Optional context fields
|
|
knowledge_base_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
query_text = sqlalchemy.Column(sqlalchemy.Text, nullable=True) # For retrieval calls
|
|
session_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
message_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
call_type = sqlalchemy.Column(sqlalchemy.String(50), nullable=True) # embedding, retrieve
|
|
|
|
|
|
class MonitoringFeedback(Base):
|
|
"""User feedback records (like/dislike) from AI Bot conversations"""
|
|
|
|
__tablename__ = 'monitoring_feedback'
|
|
|
|
id = sqlalchemy.Column(sqlalchemy.String(255), primary_key=True)
|
|
timestamp = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, index=True)
|
|
feedback_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=False, unique=True, index=True)
|
|
feedback_type = sqlalchemy.Column(sqlalchemy.Integer, nullable=False) # 1=like, 2=dislike
|
|
feedback_content = sqlalchemy.Column(sqlalchemy.Text, nullable=True) # User feedback text
|
|
inaccurate_reasons = sqlalchemy.Column(sqlalchemy.Text, nullable=True) # JSON list of inaccurate reasons
|
|
# Context fields
|
|
bot_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
bot_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
pipeline_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
pipeline_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
session_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
message_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
stream_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True, index=True)
|
|
user_id = sqlalchemy.Column(sqlalchemy.String(255), nullable=True)
|
|
platform = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) # e.g., wecom
|