44
55import asyncio
66import pathlib
7- from enum import Enum
87from typing import Any
98
109from assistant_extensions import attachments , dashboard_card , navigator
3433 load_text_include ,
3534)
3635
37- from .common import detect_assistant_role
36+ from .common import detect_assistant_role , detect_conversation_type , get_shared_conversation_id , ConversationType
3837from .config import assistant_config
3938from .conversation_share_link import ConversationKnowledgePackageManager
4039from .data import InspectorTab , LogEntryType
@@ -98,13 +97,6 @@ async def content_evaluator_factory(
9897
9998app = assistant .fastapi_app ()
10099
101-
102- class ConversationType (Enum ):
103- COORDINATOR = "coordinator"
104- TEAM = "team"
105- SHAREABLE_TEMPLATE = "shareable_template"
106-
107-
108100@assistant .events .conversation .on_created_including_mine
109101async def on_conversation_created (context : ConversationContext ) -> None :
110102 """
@@ -113,50 +105,26 @@ async def on_conversation_created(context: ConversationContext) -> None:
113105 2. Shareable Team Conversation: A template conversation that has a share URL and is never directly used
114106 3. Team Conversation(s): Individual conversations for team members created when they redeem the share URL
115107 """
116- # Get conversation to access metadata
108+
117109 conversation = await context .get_conversation ()
118110 conversation_metadata = conversation .metadata or {}
111+ share_id = conversation_metadata .get ("share_id" )
119112
120113 config = await assistant_config .get (context .assistant )
114+ conversation_type = detect_conversation_type (conversation )
121115
122- ##
123- ## Figure out what type of conversation this is.
124- ##
125-
126- conversation_type = ConversationType .COORDINATOR
127-
128- # Coordinator conversations will not have a share_id or
129- # is_team_conversation flag in the metadata. So, if they are there, we just
130- # need to decide if it's a shareable template or a team conversation.
131- share_id = conversation_metadata .get ("share_id" )
132- if conversation_metadata .get ("is_team_conversation" , False ) and share_id :
133- # If this conversation was imported from another, it indicates it's from
134- # share redemption.
135- if conversation .imported_from_conversation_id :
136- conversation_type = ConversationType .TEAM
137- # TODO: This might work better for detecting a redeemed link, but
138- # hasn't been validated.
139-
140- # if conversation_metadata.get("share_redemption") and conversation_metadata.get("share_redemption").get(
141- # "conversation_share_id"
142- # ):
143- # conversation_type = ConversationType.TEAM
144- else :
145- conversation_type = ConversationType .SHAREABLE_TEMPLATE
146-
147- ##
148- ## Handle the conversation based on its type
149- ##
150116 match conversation_type :
151117 case ConversationType .SHAREABLE_TEMPLATE :
118+
119+ # Associate the shareable template with a share ID
152120 if not share_id :
153121 logger .error ("No share ID found for shareable team conversation." )
154122 return
155-
156123 await ConversationKnowledgePackageManager .associate_conversation_with_share (context , share_id )
157124 return
158125
159126 case ConversationType .TEAM :
127+
160128 if not share_id :
161129 logger .error ("No share ID found for team conversation." )
162130 return
@@ -170,13 +138,9 @@ async def on_conversation_created(context: ConversationContext) -> None:
170138 )
171139
172140 await ConversationKnowledgePackageManager .associate_conversation_with_share (context , share_id )
173- # Set the conversation role for team conversations
174141 await ConversationKnowledgePackageManager .set_conversation_role (context , share_id , ConversationRole .TEAM )
175-
176- # Synchronize files.
177142 await ShareManager .synchronize_files_to_team_conversation (context = context , share_id = share_id )
178143
179- # Generate a welcome message.
180144 welcome_message , debug = await generate_team_welcome_message (context )
181145 await context .send_messages (
182146 NewConversationMessage (
@@ -202,11 +166,10 @@ async def on_conversation_created(context: ConversationContext) -> None:
202166
203167 case ConversationType .COORDINATOR :
204168 try :
169+ # In the beginning, we created a share...
205170 share_id = await KnowledgeTransferManager .create_share (context )
206171
207- # No default brief - let the state inspector handle displaying instructional content
208-
209- # Create a team conversation with a share URL
172+ # And it was good. So we then created a sharable conversation that we use as a template.
210173 share_url = await KnowledgeTransferManager .create_shareable_team_conversation (
211174 context = context , share_id = share_id
212175 )
@@ -218,14 +181,52 @@ async def on_conversation_created(context: ConversationContext) -> None:
218181 except Exception as e :
219182 welcome_message = f"I'm having trouble setting up your knowledge transfer. Please try again or contact support if the issue persists. { str (e )} "
220183
221- # Send the welcome message
222184 await context .send_messages (
223185 NewConversationMessage (
224186 content = welcome_message ,
225187 message_type = MessageType .chat ,
226188 )
227189 )
228190
191+ # Pop open the inspector panel.
192+ await context .send_conversation_state_event (
193+ AssistantStateEvent (
194+ state_id = "brief" ,
195+ event = "focus" ,
196+ state = None ,
197+ )
198+ )
199+
200+ @assistant .events .conversation .on_updated
201+ async def on_conversation_updated (context : ConversationContext ) -> None :
202+ """
203+ Handle conversation updates (including title changes) and sync with shareable template.
204+ """
205+ try :
206+ conversation = await context .get_conversation ()
207+ conversation_type = detect_conversation_type (conversation )
208+ if conversation_type != ConversationType .COORDINATOR :
209+ return
210+
211+ shared_conversation_id = await get_shared_conversation_id (context )
212+ if not shared_conversation_id :
213+ return
214+
215+ # Update the shareable template conversation's title if needed.
216+ try :
217+ target_context = context .for_conversation (shared_conversation_id )
218+ target_conversation = await target_context .get_conversation ()
219+ if target_conversation .title != conversation .title :
220+ await target_context .update_conversation_title (conversation .title )
221+ logger .debug (f"Updated conversation { shared_conversation_id } title from '{ target_conversation .title } ' to '{ conversation .title } '" )
222+ else :
223+ logger .debug (f"Conversation { shared_conversation_id } title already matches: '{ conversation .title } '" )
224+ except Exception as title_update_error :
225+ logger .error (f"Error updating conversation { shared_conversation_id } title: { title_update_error } " )
226+
227+ except Exception as e :
228+ logger .error (f"Error syncing conversation title: { e } " )
229+
229230
230231@assistant .events .conversation .message .chat .on_created
231232async def on_message_created (
@@ -545,3 +546,6 @@ async def on_participant_joined(
545546
546547 except Exception as e :
547548 logger .exception (f"Error handling participant join event: { e } " )
549+
550+
551+
0 commit comments