feat: Implement Qwen adapter and enhance AI platform detection
- Added a new Qwen adapter to facilitate monitoring through Playwright, leveraging internal text/chat managers. - Updated account detection logic to recognize Qwen sessions based on persisted cookies, improving binding reliability. - Relaxed authentication requirements for monitor tasks, allowing anonymous execution for certain AI platforms. - Enhanced the generic AI platform detection to include Qwen-specific logic, ensuring accurate session identification. - Modified the monitoring dashboard to include runtime state indicating if the current user's desktop client is online. - Updated various files including `account-binder.ts`, `runtime-controller.ts`, and `monitoring_service.go` to support new features and improvements.
This commit is contained in:
@@ -40,6 +40,7 @@ func NewMonitoringService(businessPool, monitoringPool *pgxpool.Pool) *Monitorin
|
||||
|
||||
type MonitoringDashboardCompositeResponse struct {
|
||||
Overview MonitoringOverview `json:"overview"`
|
||||
Runtime MonitoringDashboardRuntime `json:"runtime"`
|
||||
PlatformBreakdown []MonitoringPlatformDaily `json:"platform_breakdown"`
|
||||
BrandTimeBuckets []MonitoringTimeBucket `json:"brand_time_buckets"`
|
||||
HotQuestions []MonitoringHotQuestion `json:"hot_questions"`
|
||||
@@ -47,6 +48,10 @@ type MonitoringDashboardCompositeResponse struct {
|
||||
CitedArticles []MonitoringCitedArticle `json:"cited_articles"`
|
||||
}
|
||||
|
||||
type MonitoringDashboardRuntime struct {
|
||||
CurrentUserClientOnline bool `json:"current_user_client_online"`
|
||||
}
|
||||
|
||||
type MonitoringOverview struct {
|
||||
CollectionMode string `json:"collection_mode"`
|
||||
MentionRate *float64 `json:"mention_rate"`
|
||||
@@ -285,6 +290,11 @@ func (s *MonitoringService) DashboardComposite(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runtime, err := s.loadDashboardRuntime(ctx, actor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configuredQuestions, err := s.loadConfiguredQuestions(ctx, actor.TenantID, brand.ID, keywordID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -338,6 +348,7 @@ func (s *MonitoringService) DashboardComposite(
|
||||
|
||||
return &MonitoringDashboardCompositeResponse{
|
||||
Overview: overview,
|
||||
Runtime: runtime,
|
||||
PlatformBreakdown: platformBreakdown,
|
||||
BrandTimeBuckets: timeBuckets,
|
||||
HotQuestions: hotQuestions,
|
||||
@@ -346,6 +357,17 @@ func (s *MonitoringService) DashboardComposite(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *MonitoringService) loadDashboardRuntime(ctx context.Context, actor auth.Actor) (MonitoringDashboardRuntime, error) {
|
||||
online, err := s.hasOnlineClientForUser(ctx, actor.TenantID, actor.PrimaryWorkspaceID, actor.UserID)
|
||||
if err != nil {
|
||||
return MonitoringDashboardRuntime{}, err
|
||||
}
|
||||
|
||||
return MonitoringDashboardRuntime{
|
||||
CurrentUserClientOnline: online,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *MonitoringService) QuestionDetail(ctx context.Context, brandID, questionID int64, dateFrom, dateTo string, questionHash string, aiPlatformID *string) (*MonitoringQuestionDetailResponse, error) {
|
||||
actor := auth.MustActor(ctx)
|
||||
|
||||
@@ -502,12 +524,12 @@ func (s *MonitoringService) CollectNow(ctx context.Context, brandID int64, keywo
|
||||
platforms := resolveMonitoringPlatforms(quota.EnabledPlatforms)
|
||||
questionIDs := configuredQuestionIDs(configuredQuestions)
|
||||
|
||||
clientID, err := s.resolveCollectNowClientID(ctx, actor.TenantID, actor.PrimaryWorkspaceID, quota.PrimaryClientID)
|
||||
clientID, err := s.resolveCollectNowClientID(ctx, actor.TenantID, actor.PrimaryWorkspaceID, actor.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if clientID == nil {
|
||||
return nil, response.ErrConflict(40941, "desktop_client_offline", "primary monitoring desktop client is offline")
|
||||
return nil, response.ErrConflict(40941, "desktop_client_offline", "current user's monitoring desktop client is offline")
|
||||
}
|
||||
|
||||
if err := s.ensureClientOnline(ctx, actor.TenantID, actor.PrimaryWorkspaceID, *clientID); err != nil {
|
||||
@@ -589,47 +611,32 @@ func (s *MonitoringService) CollectNow(ctx context.Context, brandID int64, keywo
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *MonitoringService) resolveCollectNowClientID(ctx context.Context, tenantID, workspaceID int64, preferred *uuid.UUID) (*uuid.UUID, error) {
|
||||
if preferred != nil {
|
||||
online, err := s.isClientOnline(ctx, tenantID, workspaceID, *preferred)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if online {
|
||||
return preferred, nil
|
||||
}
|
||||
}
|
||||
|
||||
fallbackID, err := s.findLatestOnlineClient(ctx, tenantID, workspaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fallbackID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if preferred == nil || *preferred != *fallbackID {
|
||||
if err := s.persistPrimaryClientID(ctx, tenantID, workspaceID, *fallbackID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackID, nil
|
||||
func (s *MonitoringService) resolveCollectNowClientID(ctx context.Context, tenantID, workspaceID, userID int64) (*uuid.UUID, error) {
|
||||
return s.findLatestOnlineClientForUser(ctx, tenantID, workspaceID, userID)
|
||||
}
|
||||
|
||||
func (s *MonitoringService) findLatestOnlineClient(ctx context.Context, tenantID, workspaceID int64) (*uuid.UUID, error) {
|
||||
func (s *MonitoringService) hasOnlineClientForUser(ctx context.Context, tenantID, workspaceID, userID int64) (bool, error) {
|
||||
clientID, err := s.findLatestOnlineClientForUser(ctx, tenantID, workspaceID, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return clientID != nil, nil
|
||||
}
|
||||
|
||||
func (s *MonitoringService) findLatestOnlineClientForUser(ctx context.Context, tenantID, workspaceID, userID int64) (*uuid.UUID, error) {
|
||||
var clientID uuid.UUID
|
||||
err := s.businessPool.QueryRow(ctx, `
|
||||
SELECT id
|
||||
FROM desktop_clients
|
||||
WHERE tenant_id = $1
|
||||
AND workspace_id = $2
|
||||
AND user_id = $3
|
||||
AND revoked_at IS NULL
|
||||
AND last_seen_at IS NOT NULL
|
||||
AND last_seen_at >= NOW() - $3::interval
|
||||
AND last_seen_at >= NOW() - $4::interval
|
||||
ORDER BY last_seen_at DESC, created_at DESC, id DESC
|
||||
LIMIT 1
|
||||
`, tenantID, workspaceID, formatPgInterval(monitoringOnlineDuration)).Scan(&clientID)
|
||||
`, tenantID, workspaceID, userID, formatPgInterval(desktopClientPresenceTTL)).Scan(&clientID)
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, nil
|
||||
@@ -2463,7 +2470,7 @@ func (s *MonitoringService) ensureClientOnline(ctx context.Context, tenantID, wo
|
||||
return err
|
||||
}
|
||||
if !online {
|
||||
return response.ErrConflict(40941, "desktop_client_offline", "primary monitoring desktop client is offline")
|
||||
return response.ErrConflict(40941, "desktop_client_offline", "current user's monitoring desktop client is offline")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2485,7 +2492,7 @@ func (s *MonitoringService) isClientOnline(ctx context.Context, tenantID, worksp
|
||||
return false, response.ErrInternal(50041, "query_failed", "failed to inspect monitoring desktop client")
|
||||
}
|
||||
|
||||
if !lastSeenAt.Valid || time.Since(lastSeenAt.Time) > monitoringOnlineDuration {
|
||||
if !lastSeenAt.Valid || time.Since(lastSeenAt.Time) > desktopClientPresenceTTL {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
|
||||
Reference in New Issue
Block a user