letschatty 0.4.349__py3-none-any.whl → 0.4.351__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of letschatty might be problematic. Click here for more details.

Files changed (89) hide show
  1. letschatty/models/ai_microservices/__init__.py +4 -4
  2. letschatty/models/ai_microservices/expected_output.py +29 -2
  3. letschatty/models/ai_microservices/lambda_events.py +155 -28
  4. letschatty/models/ai_microservices/lambda_invokation_types.py +4 -1
  5. letschatty/models/ai_microservices/n8n_ai_agents_payload.py +3 -1
  6. letschatty/models/analytics/events/__init__.py +3 -3
  7. letschatty/models/analytics/events/chat_based_events/ai_agent_execution_event.py +71 -0
  8. letschatty/models/analytics/events/chat_based_events/chat_funnel.py +13 -69
  9. letschatty/models/analytics/events/company_based_events/asset_events.py +2 -9
  10. letschatty/models/analytics/events/event_type_to_classes.py +3 -7
  11. letschatty/models/analytics/events/event_types.py +50 -11
  12. letschatty/models/chat/chat.py +2 -13
  13. letschatty/models/chat/chat_with_assets.py +1 -6
  14. letschatty/models/chat/client.py +2 -0
  15. letschatty/models/chat/continuous_conversation.py +1 -1
  16. letschatty/models/company/CRM/funnel.py +33 -365
  17. letschatty/models/company/__init__.py +1 -7
  18. letschatty/models/company/assets/ai_agents_v2/ai_agents_decision_output.py +1 -1
  19. letschatty/models/company/assets/ai_agents_v2/chatty_ai_agent_in_chat.py +4 -0
  20. letschatty/models/company/assets/ai_agents_v2/chatty_ai_mode.py +2 -2
  21. letschatty/models/company/assets/ai_agents_v2/get_chat_with_prompt_response.py +1 -0
  22. letschatty/models/company/assets/ai_agents_v2/pre_qualify_config.py +28 -1
  23. letschatty/models/company/assets/automation.py +19 -10
  24. letschatty/models/company/assets/chat_assets.py +2 -3
  25. letschatty/models/company/assets/company_assets.py +0 -2
  26. letschatty/models/company/assets/sale.py +3 -3
  27. letschatty/models/company/empresa.py +1 -2
  28. letschatty/models/data_base/collection_interface.py +101 -29
  29. letschatty/models/data_base/mongo_connection.py +92 -9
  30. letschatty/models/messages/chatty_messages/schema/chatty_content/content_document.py +2 -4
  31. letschatty/models/messages/chatty_messages/schema/chatty_content/content_media.py +3 -4
  32. letschatty/models/utils/custom_exceptions/custom_exceptions.py +14 -1
  33. letschatty/services/ai_agents/smart_follow_up_context_builder_v2.py +5 -2
  34. letschatty/services/chat/chat_service.py +11 -47
  35. letschatty/services/chatty_assets/__init__.py +12 -0
  36. letschatty/services/chatty_assets/asset_service.py +190 -13
  37. letschatty/services/chatty_assets/assets_collections.py +137 -0
  38. letschatty/services/chatty_assets/base_container.py +3 -2
  39. letschatty/services/chatty_assets/base_container_with_collection.py +35 -26
  40. letschatty/services/chatty_assets/collections/__init__.py +38 -0
  41. letschatty/services/chatty_assets/collections/ai_agent_collection.py +19 -0
  42. letschatty/services/chatty_assets/collections/ai_agent_in_chat_collection.py +32 -0
  43. letschatty/services/chatty_assets/collections/ai_component_collection.py +21 -0
  44. letschatty/services/chatty_assets/collections/chain_of_thought_collection.py +30 -0
  45. letschatty/services/chatty_assets/collections/chat_collection.py +21 -0
  46. letschatty/services/chatty_assets/collections/contact_point_collection.py +21 -0
  47. letschatty/services/chatty_assets/collections/fast_answer_collection.py +21 -0
  48. letschatty/services/chatty_assets/collections/filter_criteria_collection.py +18 -0
  49. letschatty/services/chatty_assets/collections/flow_collection.py +20 -0
  50. letschatty/services/chatty_assets/collections/product_collection.py +20 -0
  51. letschatty/services/chatty_assets/collections/sale_collection.py +20 -0
  52. letschatty/services/chatty_assets/collections/source_collection.py +21 -0
  53. letschatty/services/chatty_assets/collections/tag_collection.py +19 -0
  54. letschatty/services/chatty_assets/collections/topic_collection.py +21 -0
  55. letschatty/services/chatty_assets/collections/user_collection.py +20 -0
  56. letschatty/services/chatty_assets/example_usage.py +44 -0
  57. letschatty/services/chatty_assets/services/__init__.py +37 -0
  58. letschatty/services/chatty_assets/services/ai_agent_in_chat_service.py +73 -0
  59. letschatty/services/chatty_assets/services/ai_agent_service.py +23 -0
  60. letschatty/services/chatty_assets/services/chain_of_thought_service.py +70 -0
  61. letschatty/services/chatty_assets/services/chat_service.py +25 -0
  62. letschatty/services/chatty_assets/services/contact_point_service.py +29 -0
  63. letschatty/services/chatty_assets/services/fast_answer_service.py +32 -0
  64. letschatty/services/chatty_assets/services/filter_criteria_service.py +30 -0
  65. letschatty/services/chatty_assets/services/flow_service.py +25 -0
  66. letschatty/services/chatty_assets/services/product_service.py +30 -0
  67. letschatty/services/chatty_assets/services/sale_service.py +25 -0
  68. letschatty/services/chatty_assets/services/source_service.py +28 -0
  69. letschatty/services/chatty_assets/services/tag_service.py +32 -0
  70. letschatty/services/chatty_assets/services/topic_service.py +31 -0
  71. letschatty/services/chatty_assets/services/user_service.py +32 -0
  72. letschatty/services/continuous_conversation_service/continuous_conversation_helper.py +11 -0
  73. letschatty/services/events/__init__.py +6 -0
  74. letschatty/services/events/events_manager.py +218 -1
  75. letschatty/services/factories/analytics/ai_agent_event_factory.py +161 -0
  76. letschatty/services/factories/analytics/events_factory.py +66 -30
  77. letschatty/services/factories/lambda_ai_orchestrartor/lambda_events_factory.py +46 -8
  78. letschatty/services/messages_helpers/get_caption_or_body_or_preview.py +6 -4
  79. letschatty/services/validators/analytics_validator.py +0 -11
  80. {letschatty-0.4.349.dist-info → letschatty-0.4.351.dist-info}/METADATA +1 -1
  81. {letschatty-0.4.349.dist-info → letschatty-0.4.351.dist-info}/RECORD +83 -53
  82. letschatty/models/analytics/events/chat_based_events/chat_client.py +0 -19
  83. letschatty/models/company/integrations/product_sync_status.py +0 -28
  84. letschatty/models/company/integrations/shopify/company_shopify_integration.py +0 -62
  85. letschatty/models/company/integrations/shopify/shopify_product_sync_status.py +0 -18
  86. letschatty/models/company/integrations/shopify/shopify_webhook_topics.py +0 -40
  87. letschatty/models/company/integrations/sync_status_enum.py +0 -9
  88. {letschatty-0.4.349.dist-info → letschatty-0.4.351.dist-info}/LICENSE +0 -0
  89. {letschatty-0.4.349.dist-info → letschatty-0.4.351.dist-info}/WHEEL +0 -0
@@ -1,10 +1,10 @@
1
1
  from .expected_output import ExpectedOutputQualityTest, ExpectedOutputSmartTag
2
2
  from .lambda_events import (
3
- IncomingMessageCallbackEvent, QualityTestCallbackEvent, SmartTaggingCallbackEvent,
4
- QualityTestInteractionCallbackEvent, IncomingMessageEvent, QualityTestEvent,
5
- AllQualityTestEvent, FollowUpEvent, SmartTaggingEvent, QualityTestEventData, AllQualityTestEventData,ChatData,
3
+ IncomingMessageAIDecision, QualityTestCallbackEvent, SmartTaggingCallbackEvent,
4
+ QualityTestInteractionCallbackEvent, QualityTestEvent,
5
+ AllQualityTestEvent, SmartTaggingEvent, QualityTestEventData, AllQualityTestEventData,ChatData,
6
6
  ComparisonAnalysisCallbackMetadata, InteractionCallbackMetadata, SmartTaggingCallbackMetadata,
7
- SmartTaggingPromptEvent
7
+ SmartTaggingPromptEvent, UpdateAIAgentPrequalStatusInChatEvent, UpdateAIAgentPrequalStatusInChatEventData
8
8
  )
9
9
  from .lambda_invokation_types import InvokationType, LambdaAiEvent
10
10
  from .openai_payloads import OpenaiPayload, N8nPayload
@@ -6,9 +6,10 @@ from typing import List, Optional, Literal
6
6
  from datetime import datetime
7
7
 
8
8
  from letschatty.models.utils.types.message_types import MessageType
9
- from ...models.company.assets.ai_agents_v2.ai_agents_decision_output import ChainOfThoughtInChatRequest, IncomingMessageAIDecision, IncomingMessageDecisionAction
9
+ from ...models.company.assets.ai_agents_v2.ai_agents_decision_output import ChainOfThoughtInChatRequest, IncomingMessageAIDecision, IncomingMessageDecisionAction, SmartFollowUpDecision, SmartFollowUpDecisionAction
10
10
  from ...models.company.assets.automation import Automation
11
11
  from ...models.company.form_field import CollectedData
12
+ from ...models.chat.chat import Area
12
13
 
13
14
  class ExpectedOutputQualityTest(BaseModel):
14
15
  accuracy: float = Field(description="The accuracy of the comparison analysis")
@@ -194,4 +195,30 @@ class ExpectedOutputIncomingMessage(BaseModel):
194
195
 
195
196
 
196
197
  class ExpectedOutputSmartFollowUp(BaseModel):
197
- action: Optional[str] = None
198
+ action: SmartFollowUpDecisionAction
199
+ messages: List[str] = Field(description="Array of message strings to send to the customer. Required for send/suggest actions, optional for escalate action, empty array for skip/remove/postpone actions.")
200
+ chain_of_thought: ChainOfThoughtInChatRequest = Field(description="REQUIRED: Your reasoning process and response decision explanation")
201
+ next_call_time: Optional[datetime] = Field(default=None, description="The next call time for the smart follow up (required for postpone_delta_time action)")
202
+ reason: Optional[str] = Field(default=None, description="Reason for the decision (especially for postpone/postponed actions)")
203
+ area: Optional[Area] = Field(default=None, description="The area to move the chat after the decision")
204
+
205
+ def to_smart_follow_up_decision_output(self) -> SmartFollowUpDecision:
206
+ messages_drafts = [
207
+ MessageDraft(
208
+ type=MessageType.TEXT,
209
+ content=ChattyContentText(body=message),
210
+ is_incoming_message=False
211
+ )
212
+ for message in self.messages
213
+ ]
214
+ smart_follow_up_decision = SmartFollowUpDecision(
215
+ action=self.action,
216
+ next_call_time=self.next_call_time,
217
+ messages=messages_drafts,
218
+ chain_of_thought=self.chain_of_thought,
219
+ reason=self.reason,
220
+ area=self.area
221
+ )
222
+ return smart_follow_up_decision
223
+
224
+
@@ -1,17 +1,19 @@
1
+ from letschatty.models.company.assets.ai_agents_v2.chatty_ai_agent_in_chat import HumanInterventionReason
1
2
  from letschatty.models.company.assets.chat_assets import ChainOfThoughtInChatTrigger
2
3
  from pydantic import BaseModel, Field, model_validator
3
- from typing import Dict, Any, List, Optional
4
+ from typing import Dict, Any, List, Optional, TYPE_CHECKING
4
5
 
5
6
  from letschatty.models.base_models.ai_agent_component import AiAgentComponentType
7
+ from letschatty.models.company.assets.ai_agents_v2.statuses import DataCollectionStatus, PreQualifyStatus
6
8
  from letschatty.models.utils.types.identifier import StrObjectId
7
9
  from .lambda_invokation_types import InvokationType, LambdaAiEvent
8
- from .expected_output import ExpectedOutputIncomingMessage, ExpectedOutputSmartTag, ExpectedOutputQualityTest, ExpectedOutputSmartFollowUp, IncomingMessageAIDecision
9
- from ...models.company.assets.ai_agents_v2.ai_agents_decision_output import IncomingMessageDecisionAction
10
- from ...models.company.assets.ai_agents_v2.chatty_ai_agent_in_chat import HumanInterventionReason
10
+ from .expected_output import ExpectedOutputIncomingMessage, ExpectedOutputSmartFollowUp, ExpectedOutputSmartTag, ExpectedOutputQualityTest, IncomingMessageAIDecision
11
+ from ...models.company.assets.ai_agents_v2.ai_agents_decision_output import IncomingMessageDecisionAction, SmartFollowUpDecision
11
12
 
12
13
  class SmartTaggingCallbackMetadata(BaseModel):
13
14
  chat_id: StrObjectId
14
15
  company_id: StrObjectId
16
+ trigger: ChainOfThoughtInChatTrigger
15
17
 
16
18
  class ComparisonAnalysisCallbackMetadata(BaseModel):
17
19
  test_case_id: StrObjectId
@@ -23,21 +25,8 @@ class InteractionCallbackMetadata(BaseModel):
23
25
  ai_agent_id: StrObjectId
24
26
  company_id: StrObjectId
25
27
  interaction_index: int
26
-
27
- class IncomingMessageCallbackMetadata(BaseModel):
28
- chat_id: StrObjectId
29
- company_id: StrObjectId
30
- ai_agent_id: StrObjectId
31
- cot_id: StrObjectId
32
- trigger: str
33
- # Trigger info is in: incoming_message_ids for user_message,
34
- # triggered_by_user_id for manual_trigger, smart_follow_up_id for follow_up
35
- triggered_by_user_id: Optional[StrObjectId] = None
36
-
37
- class IncomingMessageCallbackEvent(LambdaAiEvent):
38
- type: InvokationType = InvokationType.INCOMING_MESSAGE_CALLBACK
39
- data: IncomingMessageAIDecision
40
- callback_metadata: IncomingMessageCallbackMetadata
28
+ trigger: ChainOfThoughtInChatTrigger
29
+ chain_of_thought_id: StrObjectId
41
30
 
42
31
  class QualityTestCallbackEvent(LambdaAiEvent):
43
32
  type: InvokationType = InvokationType.SINGLE_QUALITY_TEST_CALLBACK
@@ -55,6 +44,50 @@ class QualityTestInteractionCallbackEvent(LambdaAiEvent):
55
44
  data["data"]["chain_of_thought"]["chatty_ai_agent_id"] = data["callback_metadata"]["ai_agent_id"]
56
45
  return data
57
46
 
47
+ @model_validator(mode="before")
48
+ @classmethod
49
+ def set_chain_of_thought_id(cls, values: Dict[str, Any]):
50
+ """
51
+ Get the chain_of_thought_id from callback_metadata and set it in data.chain_of_thought.id
52
+
53
+ Handles both cases:
54
+ - data is a JSON string (needs parsing)
55
+ - data is already a dict
56
+ """
57
+ import json
58
+
59
+ data = values.get("data")
60
+ callback_metadata = values.get("callback_metadata")
61
+
62
+ if not data or not callback_metadata:
63
+ return values
64
+
65
+ # Get chain_of_thought_id from callback_metadata (could be dict or object)
66
+ if isinstance(callback_metadata, dict):
67
+ chain_of_thought_id = callback_metadata.get("chain_of_thought_id")
68
+ else:
69
+ chain_of_thought_id = getattr(callback_metadata, "chain_of_thought_id", None)
70
+
71
+ if not chain_of_thought_id:
72
+ return values
73
+
74
+ # Parse data if it's a JSON string
75
+ if isinstance(data, str):
76
+ try:
77
+ data = json.loads(data)
78
+ values["data"] = data
79
+ except (json.JSONDecodeError, TypeError):
80
+ return values
81
+
82
+ # If data is a dict, set the id in chain_of_thought
83
+ if isinstance(data, dict):
84
+ chain_of_thought = data.get("chain_of_thought")
85
+ if isinstance(chain_of_thought, dict):
86
+ chain_of_thought["id"] = chain_of_thought_id
87
+
88
+ return values
89
+
90
+
58
91
  class SmartTaggingCallbackEvent(LambdaAiEvent):
59
92
  type: InvokationType = InvokationType.SMART_TAGGING_CALLBACK
60
93
  data: ExpectedOutputSmartTag
@@ -149,11 +182,9 @@ class DoubleCheckerCallbackMetadata(BaseModel):
149
182
  company_id: StrObjectId
150
183
  ai_agent_id: StrObjectId
151
184
  incoming_messages_ids: List[str]
152
- # Trigger info
153
- trigger: str
154
- # Trigger info is in: incoming_messages_ids for user_message,
155
- # triggered_by_user_id for manual_trigger, smart_follow_up_id for follow_up
156
- triggered_by_user_id: Optional[StrObjectId] = None
185
+ trigger: ChainOfThoughtInChatTrigger
186
+ chain_of_thought_id: StrObjectId
187
+
157
188
 
158
189
  class DoubleCheckerForIncomingMessagesAnswerEvent(LambdaAiEvent):
159
190
  type: InvokationType = InvokationType.DOUBLE_CHECKER_FOR_INCOMING_MESSAGES_ANSWER
@@ -161,13 +192,73 @@ class DoubleCheckerForIncomingMessagesAnswerEvent(LambdaAiEvent):
161
192
 
162
193
  class DoubleCheckerForIncomingMessagesAnswerCallbackEvent(LambdaAiEvent):
163
194
  type: InvokationType = InvokationType.DOUBLE_CHECKER_FOR_INCOMING_MESSAGES_ANSWER_CALLBACK
164
- data: DoubleCheckerForIncomingMessagesAnswerData
195
+ data: ExpectedOutputIncomingMessage
165
196
  callback_metadata: DoubleCheckerCallbackMetadata
166
197
 
167
- class DoubleCheckerForIncomingMessagesAnswerCallbackMetadata(BaseModel):
198
+ @model_validator(mode="before")
199
+ @classmethod
200
+ def set_chain_of_thought_id(cls, values: Dict[str, Any]):
201
+ """
202
+ Get the chain_of_thought_id from callback_metadata and set it in data.chain_of_thought.id
203
+
204
+ Handles both cases:
205
+ - data is a JSON string (needs parsing)
206
+ - data is already a dict
207
+ """
208
+ import json
209
+
210
+ data = values.get("data")
211
+ callback_metadata = values.get("callback_metadata")
212
+
213
+ if not data or not callback_metadata:
214
+ return values
215
+
216
+ # Get chain_of_thought_id from callback_metadata (could be dict or object)
217
+ if isinstance(callback_metadata, dict):
218
+ chain_of_thought_id = callback_metadata.get("chain_of_thought_id")
219
+ else:
220
+ chain_of_thought_id = getattr(callback_metadata, "chain_of_thought_id", None)
221
+
222
+ if not chain_of_thought_id:
223
+ return values
224
+
225
+ # Parse data if it's a JSON string
226
+ if isinstance(data, str):
227
+ try:
228
+ data = json.loads(data)
229
+ values["data"] = data
230
+ except (json.JSONDecodeError, TypeError):
231
+ return values
232
+
233
+ # If data is a dict, set the id in chain_of_thought
234
+ if isinstance(data, dict):
235
+ chain_of_thought = data.get("chain_of_thought")
236
+ if isinstance(chain_of_thought, dict):
237
+ chain_of_thought["id"] = chain_of_thought_id
238
+
239
+ return values
240
+
241
+
242
+
243
+ # Smart Follow-Up Decision Output Events
244
+
245
+ class SmartFollowUpDecisionOutputData(BaseModel):
246
+ """Data for smart follow-up decision output"""
168
247
  chat_id: StrObjectId
169
248
  company_id: StrObjectId
170
249
  ai_agent_id: StrObjectId
250
+ smart_follow_up_output: ExpectedOutputSmartFollowUp
251
+ smart_follow_up_id: Optional[StrObjectId] = None
252
+
253
+ class SmartFollowUpDecisionOutputEvent(LambdaAiEvent):
254
+ """
255
+ Event for smart follow-up decision output.
256
+
257
+ Similar to incoming message decision but for follow-ups.
258
+ Bypasses double checker and sends directly to API.
259
+ """
260
+ type: InvokationType = InvokationType.SMART_FOLLOW_UP_DECISION_OUTPUT
261
+ data: SmartFollowUpDecisionOutputData
171
262
 
172
263
 
173
264
  # New AI Agent Context Building Events (for new architecture)
@@ -202,7 +293,7 @@ class GetChatWithPromptForFollowUpEventData(BaseModel):
202
293
  """Data for get chat with prompt for follow-up event"""
203
294
  chat_id: StrObjectId
204
295
  company_id: StrObjectId
205
- smart_follow_up_id: StrObjectId
296
+ smart_follow_up_id: Optional[StrObjectId] = Field(default=None, description="Smart follow-up ID")
206
297
  # Trigger information
207
298
  trigger: ChainOfThoughtInChatTrigger
208
299
  # trigger_id is derived from: smart_follow_up_id
@@ -318,11 +409,29 @@ class UpdateAIAgentModeInChatEvent(LambdaAiEvent):
318
409
  data: UpdateAIAgentModeInChatEventData
319
410
 
320
411
 
412
+ class UpdateAIAgentPrequalStatusInChatEventData(BaseModel):
413
+ """Data for updating pre-qualification and data collection status in a chat"""
414
+ chat_id: StrObjectId
415
+ company_id: StrObjectId
416
+ data_collection_status: Optional[DataCollectionStatus] = Field(default=None, description="New data collection status")
417
+ pre_qualify_status: Optional[PreQualifyStatus] = Field(default=None, description="New pre-qualification status")
418
+ updated_by: Optional[StrObjectId] = Field(default=None, description="User who updated the status")
419
+
420
+
421
+ class UpdateAIAgentPrequalStatusInChatEvent(LambdaAiEvent):
422
+ """
423
+ Event to update data_collection_status and/or pre_qualify_status for a chat's AI agent.
424
+ """
425
+ type: InvokationType = InvokationType.UPDATE_AI_AGENT_PREQUAL_STATUS_IN_CHAT
426
+ data: UpdateAIAgentPrequalStatusInChatEventData
427
+
428
+
321
429
  class EscalateAIAgentInChatEventData(BaseModel):
322
430
  """Data for escalating an AI agent (requiring human intervention)"""
323
431
  chat_id: StrObjectId
324
432
  company_id: StrObjectId
325
- reason: str = Field(description="Reason for escalation (e.g., 'Complex technical issue', 'Customer request')")
433
+ reason: HumanInterventionReason = Field(description="Reason for escalation", default=HumanInterventionReason.SOMETHING_WENT_WRONG)
434
+ message: Optional[str] = Field(default="El agente de IA requiere de tu intervención", description="The message to display to the user if any")
326
435
 
327
436
 
328
437
  class EscalateAIAgentInChatEvent(LambdaAiEvent):
@@ -354,6 +463,24 @@ class UnescalateAIAgentInChatEvent(LambdaAiEvent):
354
463
  data: UnescalateAIAgentInChatEventData
355
464
 
356
465
 
466
+
467
+ class GetChainOfThoughtsByChatIdEventData(BaseModel):
468
+ """Data for getting chain of thoughts by chat ID"""
469
+ chat_id: StrObjectId
470
+ company_id: StrObjectId
471
+ skip: int = Field(default=0, description="Number of results to skip for pagination")
472
+ limit: int = Field(default=10, description="Number of results to return")
473
+
474
+
475
+ class GetChainOfThoughtsByChatIdEvent(LambdaAiEvent):
476
+ """
477
+ Event to get chain of thoughts for a chat.
478
+
479
+ Returns a list of chain of thoughts associated with the given chat ID,
480
+ sorted by created_at (newest first), with pagination support via skip.
481
+ """
482
+ type: InvokationType = InvokationType.GET_CHAIN_OF_THOUGHTS_BY_CHAT_ID
483
+ data: GetChainOfThoughtsByChatIdEventData
357
484
  # Launch Events
358
485
 
359
486
  class LaunchCommunicationEventData(BaseModel):
@@ -11,13 +11,14 @@ class InvokationType(StrEnum):
11
11
  SMART_TAGGING_PROMPT = "smart_tagging_prompt"
12
12
  QUALITY_TEST_INTERACTION = "quality_test_interaction"
13
13
  # Callback-specific types
14
- INCOMING_MESSAGE_CALLBACK = "incoming_message_callback"
15
14
  SINGLE_QUALITY_TEST_CALLBACK = "single_quality_test_callback"
16
15
  SMART_TAGGING_CALLBACK = "smart_tagging_callback"
17
16
  QUALITY_TESTS_FOR_UPDATED_AI_COMPONENT = "quality_tests_for_updated_ai_component"
18
17
  FIX_BUGGED_AI_AGENTS_CALLS_IN_CHATS = "fix_bugged_ai_agents_calls_in_chats"
19
18
  DOUBLE_CHECKER_FOR_INCOMING_MESSAGES_ANSWER = "double_checker_for_incoming_messages_answer"
20
19
  DOUBLE_CHECKER_FOR_INCOMING_MESSAGES_ANSWER_CALLBACK = "double_checker_for_incoming_messages_answer_callback"
20
+ # Decision output events
21
+ SMART_FOLLOW_UP_DECISION_OUTPUT = "smart_follow_up_decision_output"
21
22
  # AI Agent context building events (new architecture)
22
23
  GET_CHAT_WITH_PROMPT_INCOMING_MESSAGE = "get_chat_with_prompt_incoming_message"
23
24
  GET_CHAT_WITH_PROMPT_FOLLOW_UP = "get_chat_with_prompt_follow_up"
@@ -28,8 +29,10 @@ class InvokationType(StrEnum):
28
29
  ASSIGN_AI_AGENT_TO_CHAT = "assign_ai_agent_to_chat"
29
30
  REMOVE_AI_AGENT_FROM_CHAT = "remove_ai_agent_from_chat"
30
31
  UPDATE_AI_AGENT_MODE_IN_CHAT = "update_ai_agent_mode_in_chat"
32
+ UPDATE_AI_AGENT_PREQUAL_STATUS_IN_CHAT = "update_ai_agent_prequal_status_in_chat"
31
33
  ESCALATE_AI_AGENT_IN_CHAT = "escalate_ai_agent_in_chat"
32
34
  UNESCALATE_AI_AGENT_IN_CHAT = "unescalate_ai_agent_in_chat"
35
+ GET_CHAIN_OF_THOUGHTS_BY_CHAT_ID = "get_chain_of_thoughts_by_chat_id"
33
36
  # Launch events
34
37
  LAUNCH_COMMUNICATION = "launch_communication"
35
38
  LAUNCH_WELCOME_KIT = "launch_welcome_kit"
@@ -1,3 +1,4 @@
1
+ from letschatty.models.company.assets.chat_assets import ChainOfThoughtInChatTrigger
1
2
  from pydantic import BaseModel, Field
2
3
  from letschatty.models import StrObjectId
3
4
  from letschatty.models.company.assets.ai_agents_v2.chatty_ai_agent import N8NWorkspaceAgentType
@@ -10,4 +11,5 @@ class SmartFollowUpN8NPayload(BaseModel):
10
11
  class ManualTriggerN8NPayload(BaseModel):
11
12
  chat_id: StrObjectId = Field(description="The id of the chat")
12
13
  company_id: StrObjectId = Field(description="The id of the company")
13
- n8n_agent_type: N8NWorkspaceAgentType = Field(description="The type of agent to redirect the message to")
14
+ n8n_agent_type: N8NWorkspaceAgentType = Field(description="The type of agent to redirect the message to")
15
+ trigger: ChainOfThoughtInChatTrigger = Field(default=ChainOfThoughtInChatTrigger.MANUAL_TRIGGER)
@@ -3,9 +3,8 @@ from .chat_based_events.contact_point import ContactPointEvent, ContactPointData
3
3
  from .chat_based_events.business_area import ChatBusinessAreaEvent, BusinessAreaData
4
4
  from .chat_based_events.workflow import WorkflowEvent, WorkflowEventData
5
5
  from .chat_based_events.chat_status import ChatStatusEvent, ChatStatusEventData, ChatStatusModification, ChatCreatedFrom
6
- from .chat_based_events.chat_client import ChatClientEvent
7
6
  from .chat_based_events.chat_funnel import ChatFunnelEvent, FunnelEventData
8
- from ...company.CRM.funnel import ChatFunnel, StageTransition, ActiveFunnel
7
+ from ...company.CRM.funnel import ClientFunnel, StageTransition
9
8
  from .company_based_events.user_events import UserEvent, UserEventData
10
9
  from .chat_based_events.quality_scoring import QualityScoringEvent
11
10
  from .chat_based_events.message import MessageEvent, MessageData
@@ -29,4 +28,5 @@ from ...messages.chatty_messages import ChattyMessage
29
28
  from ...company.CRM.funnel import Funnel, FunnelStage
30
29
  from ...utils.types import Status
31
30
  from .chat_based_events.chat_based_event import CustomerEventData
32
- from .chat_based_events.ai_agent_chat import ChattyAIChatEvent, ChattyAIChatData
31
+ from .chat_based_events.ai_agent_chat import ChattyAIChatEvent, ChattyAIChatData
32
+ from .chat_based_events.ai_agent_execution_event import AIAgentExecutionEvent, AIAgentExecutionEventData
@@ -0,0 +1,71 @@
1
+ """AI Agent Execution Events - Track AI agent lifecycle and decision-making"""
2
+
3
+ from ..base import Event, EventData
4
+ from ..event_types import EventType
5
+ from .chat_based_event import CustomerEventData
6
+ from typing import ClassVar, Optional, Dict, Any
7
+ from ....utils.types.identifier import StrObjectId
8
+
9
+
10
+ class AIAgentExecutionEventData(CustomerEventData):
11
+ """Data for AI agent execution events"""
12
+ ai_agent_id: StrObjectId
13
+ chain_of_thought_id: StrObjectId
14
+ trigger: str # USER_MESSAGE, FOLLOW_UP, MANUAL_TRIGGER, RETRY
15
+ decision_type: Optional[str] = None # send, suggest, escalate, skip
16
+ error_message: Optional[str] = None
17
+ duration_ms: Optional[int] = None
18
+ user_rating: Optional[int] = None # 1-5 stars
19
+ metadata: Optional[Dict[str, Any]] = None
20
+
21
+
22
+ class AIAgentExecutionEvent(Event):
23
+ """
24
+ Events for AI agent execution lifecycle.
25
+
26
+ This event type covers the entire AI agent decision-making process,
27
+ from trigger to final decision, including all intermediate steps.
28
+ """
29
+ data: AIAgentExecutionEventData
30
+
31
+ VALID_TYPES: ClassVar[set] = {
32
+ # Trigger events
33
+ EventType.CHATTY_AI_AGENT_IN_CHAT_TRIGGER_USER_MESSAGE,
34
+ EventType.CHATTY_AI_AGENT_IN_CHAT_TRIGGER_FOLLOW_UP,
35
+ EventType.CHATTY_AI_AGENT_IN_CHAT_TRIGGER_MANUAL,
36
+ EventType.CHATTY_AI_AGENT_IN_CHAT_TRIGGER_RETRY,
37
+
38
+ # State events
39
+ EventType.CHATTY_AI_AGENT_IN_CHAT_STATE_PROCESSING_STARTED,
40
+ EventType.CHATTY_AI_AGENT_IN_CHAT_STATE_CALL_STARTED,
41
+ EventType.CHATTY_AI_AGENT_IN_CHAT_STATE_ESCALATED,
42
+ EventType.CHATTY_AI_AGENT_IN_CHAT_STATE_UNESCALATED,
43
+
44
+ # Call events
45
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALL_GET_CHAT_WITH_PROMPT,
46
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALL_TAGGER,
47
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALL_DOUBLE_CHECKER,
48
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALL_DEBUGGER,
49
+
50
+ # Callback events
51
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALLBACK_GET_CHAT_WITH_PROMPT,
52
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALLBACK_TAGGER,
53
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALLBACK_DOUBLE_CHECKER,
54
+ EventType.CHATTY_AI_AGENT_IN_CHAT_CALLBACK_OUTPUT_RECEIVED,
55
+
56
+ # Decision events
57
+ EventType.CHATTY_AI_AGENT_IN_CHAT_DECISION_SEND,
58
+ EventType.CHATTY_AI_AGENT_IN_CHAT_DECISION_SUGGEST,
59
+ EventType.CHATTY_AI_AGENT_IN_CHAT_DECISION_ESCALATE,
60
+ EventType.CHATTY_AI_AGENT_IN_CHAT_DECISION_SKIP,
61
+ EventType.CHATTY_AI_AGENT_IN_CHAT_DECISION_SENT_TO_API,
62
+ EventType.CHATTY_AI_AGENT_IN_CHAT_DECISION_COMPLETED,
63
+
64
+ # Error events
65
+ EventType.CHATTY_AI_AGENT_IN_CHAT_ERROR_CALL_FAILED,
66
+ EventType.CHATTY_AI_AGENT_IN_CHAT_ERROR_CALL_CANCELLED,
67
+ EventType.CHATTY_AI_AGENT_IN_CHAT_ERROR_VALIDATION_FAILED,
68
+
69
+ # Rating events
70
+ EventType.CHATTY_AI_AGENT_IN_CHAT_RATING_RECEIVED,
71
+ }
@@ -4,93 +4,37 @@ from ..base import Event
4
4
  from ..event_types import EventType
5
5
  from .chat_based_event import CustomerEventData
6
6
  from ....utils.types.identifier import StrObjectId
7
- from ....company.CRM.funnel import StageTransition
7
+ from ....company.CRM.funnel import ClientFunnel, StageTransition
8
8
  import json
9
9
 
10
-
11
10
  class FunnelEventData(CustomerEventData):
12
- """
13
- Event data for chat funnel events.
14
-
15
- Fields required per event type:
16
- - STARTED: funnel_id, chat_funnel_id, stage_transition
17
- - STAGE_CHANGED: funnel_id, chat_funnel_id, stage_transition (with time_in_previous_stage_seconds)
18
- - COMPLETED: funnel_id, chat_funnel_id, time_in_funnel_seconds, time_in_last_stage_seconds
19
- - ABANDONED: funnel_id, chat_funnel_id, time_in_funnel_seconds, time_in_last_stage_seconds
20
- """
21
- funnel_id: StrObjectId = Field(description="The funnel the chat is in")
22
- chat_funnel_id: StrObjectId = Field(description="Reference to the ChatFunnel record")
23
-
24
- # For STARTED and STAGE_CHANGED events
25
- stage_transition: Optional[StageTransition] = Field(
26
- default=None,
27
- description="The stage transition details (for STARTED and STAGE_CHANGED)"
28
- )
29
-
30
- # For COMPLETED and ABANDONED events
31
- time_in_funnel_seconds: Optional[int] = Field(
32
- default=None,
33
- description="Total time spent in the funnel (for COMPLETED and ABANDONED)"
34
- )
35
- time_in_last_stage_seconds: Optional[int] = Field(
36
- default=None,
37
- description="Time spent in the last stage before completion/abandonment"
38
- )
39
-
11
+ funnel_id: StrObjectId
12
+ funnel_stage_transition: StageTransition
13
+ time_in_funnel_seconds: Optional[int] = None
40
14
 
41
15
  class ChatFunnelEvent(Event):
42
- """
43
- Event for tracking chat funnel lifecycle.
44
-
45
- Events:
46
- - CHAT_FUNNEL_STARTED: Chat entered a funnel
47
- - CHAT_FUNNEL_STAGE_CHANGED: Chat moved between stages
48
- - CHAT_FUNNEL_COMPLETED: Chat completed the funnel
49
- - CHAT_FUNNEL_ABANDONED: Chat abandoned the funnel
50
- """
16
+ """Event for tracking chat funnel stage transitions"""
51
17
  data: FunnelEventData
52
18
 
19
+ # Define valid event types for this event class
53
20
  VALID_TYPES: ClassVar[set] = {
21
+ EventType.CHAT_FUNNEL_UPDATED,
54
22
  EventType.CHAT_FUNNEL_STARTED,
55
- EventType.CHAT_FUNNEL_STAGE_CHANGED,
56
23
  EventType.CHAT_FUNNEL_COMPLETED,
57
24
  EventType.CHAT_FUNNEL_ABANDONED
58
25
  }
59
26
 
27
+
60
28
  @field_validator('data')
61
29
  def validate_data_fields(cls, v: FunnelEventData, info: ValidationInfo):
62
30
  """Validate that appropriate fields are set based on event type"""
63
- event_type = info.data.get('type')
64
-
65
- # STARTED: requires stage_transition
66
- if event_type == EventType.CHAT_FUNNEL_STARTED:
67
- if v.stage_transition is None:
68
- raise ValueError("stage_transition must be set for CHAT_FUNNEL_STARTED events")
69
-
70
- # STAGE_CHANGED: requires stage_transition with time_in_previous_stage_seconds
71
- elif event_type == EventType.CHAT_FUNNEL_STAGE_CHANGED:
72
- if v.stage_transition is None:
73
- raise ValueError("stage_transition must be set for CHAT_FUNNEL_STAGE_CHANGED events")
74
- if v.stage_transition.time_in_previous_stage_seconds is None:
75
- raise ValueError("time_in_previous_stage_seconds must be set in stage_transition for CHAT_FUNNEL_STAGE_CHANGED events")
76
-
77
- # COMPLETED: requires time metrics
78
- elif event_type == EventType.CHAT_FUNNEL_COMPLETED:
79
- if v.time_in_funnel_seconds is None:
80
- raise ValueError("time_in_funnel_seconds must be set for CHAT_FUNNEL_COMPLETED events")
81
- if v.time_in_last_stage_seconds is None:
82
- raise ValueError("time_in_last_stage_seconds must be set for CHAT_FUNNEL_COMPLETED events")
83
-
84
- # ABANDONED: requires time metrics
85
- elif event_type == EventType.CHAT_FUNNEL_ABANDONED:
86
- if v.time_in_funnel_seconds is None:
87
- raise ValueError("time_in_funnel_seconds must be set for CHAT_FUNNEL_ABANDONED events")
88
- if v.time_in_last_stage_seconds is None:
89
- raise ValueError("time_in_last_stage_seconds must be set for CHAT_FUNNEL_ABANDONED events")
90
-
31
+ if info.data.get('type') == EventType.CHAT_FUNNEL_UPDATED and v.time_in_funnel_seconds is None:
32
+ raise ValueError("time_in_funnel_seconds must be set for CHAT_FUNNEL_UPDATED events")
33
+ if info.data.get('type') == EventType.CHAT_FUNNEL_UPDATED and v.funnel_stage_transition.time_in_previous_stage_seconds is None:
34
+ raise ValueError("time_in_previous_stage_seconds must be set for CHAT_FUNNEL_UPDATED events")
91
35
  return v
92
36
 
93
37
  def model_dump_json(self, *args, **kwargs):
94
38
  dump = json.loads(super().model_dump_json(*args, **kwargs))
95
39
  dump['data'] = self.data.model_dump_json()
96
- return dump
40
+ return dump
@@ -58,9 +58,6 @@ class AssetEvent(Event):
58
58
  EventType.FUNNEL_STAGE_CREATED,
59
59
  EventType.FUNNEL_STAGE_UPDATED,
60
60
  EventType.FUNNEL_STAGE_DELETED,
61
- EventType.FUNNEL_MEMBER_ADDED,
62
- EventType.FUNNEL_MEMBER_UPDATED,
63
- EventType.FUNNEL_MEMBER_REMOVED,
64
61
  EventType.WORKFLOW_CREATED,
65
62
  EventType.WORKFLOW_UPDATED,
66
63
  EventType.WORKFLOW_DELETED,
@@ -73,9 +70,6 @@ class AssetEvent(Event):
73
70
  EventType.FILTER_CRITERIA_CREATED,
74
71
  EventType.FILTER_CRITERIA_UPDATED,
75
72
  EventType.FILTER_CRITERIA_DELETED,
76
- EventType.FORM_FIELD_CREATED,
77
- EventType.FORM_FIELD_UPDATED,
78
- EventType.FORM_FIELD_DELETED,
79
73
  EventType.COMPANY_CREATED,
80
74
  EventType.COMPANY_UPDATED,
81
75
  EventType.COMPANY_DELETED
@@ -83,9 +77,8 @@ class AssetEvent(Event):
83
77
 
84
78
  @model_validator(mode='after')
85
79
  def validate_data_fields(self):
86
- # Asset is not required for deleted/removed events
87
- if "deleted" not in self.type.value and "removed" not in self.type.value and not self.data.asset:
88
- raise ValueError("asset must be set for all events except DELETED/REMOVED")
80
+ if "deleted" not in self.type.value and not self.data.asset:
81
+ raise ValueError("asset must be set for all events except DELETED")
89
82
  return self
90
83
 
91
84
  def model_dump_json(self, *args, **kwargs):
@@ -8,7 +8,6 @@ EVENT_TO_TYPE_CLASSES = {
8
8
  EventType.CHAT_CREATED : ChatStatusEvent,
9
9
  EventType.CHAT_STATUS_UPDATED : ChatStatusEvent,
10
10
  EventType.CHAT_DELETED : ChatStatusEvent,
11
- EventType.CHAT_CLIENT_UPDATED : ChatClientEvent,
12
11
  #TAGS
13
12
  EventType.TAG_ASSIGNED : TagChatEvent,
14
13
  EventType.TAG_REMOVED : TagChatEvent,
@@ -42,9 +41,10 @@ EVENT_TO_TYPE_CLASSES = {
42
41
  #CONTINUOUS CONVERSATION
43
42
  EventType.CONTINUOUS_CONVERSATION_CREATED : ContinuousConversationEvent,
44
43
  EventType.CONTINUOUS_CONVERSATION_UPDATED : ContinuousConversationEvent,
45
- #CHAT FUNNEL EVENTS
44
+ #FUNNEL STAGES
45
+ # Funnel-level events
46
46
  EventType.CHAT_FUNNEL_STARTED : ChatFunnelEvent,
47
- EventType.CHAT_FUNNEL_STAGE_CHANGED : ChatFunnelEvent,
47
+ EventType.CHAT_FUNNEL_UPDATED : ChatFunnelEvent,
48
48
  EventType.CHAT_FUNNEL_COMPLETED : ChatFunnelEvent,
49
49
  EventType.CHAT_FUNNEL_ABANDONED : ChatFunnelEvent,
50
50
 
@@ -86,10 +86,6 @@ EVENT_TO_TYPE_CLASSES = {
86
86
  EventType.WORKFLOW_CREATED : AssetEvent,
87
87
  EventType.WORKFLOW_UPDATED : AssetEvent,
88
88
  EventType.WORKFLOW_DELETED : AssetEvent,
89
- #FORM FIELDS
90
- EventType.FORM_FIELD_CREATED : AssetEvent,
91
- EventType.FORM_FIELD_UPDATED : AssetEvent,
92
- EventType.FORM_FIELD_DELETED : AssetEvent,
93
89
  #AGENTS
94
90
  EventType.USER_CREATED : UserEvent,
95
91
  EventType.USER_UPDATED : UserEvent,