@@ -84,41 +84,167 @@ def list_checkpoints(
8484 help = "Enable verbose output" ,
8585 ),
8686):
87+ """List all available checkpoints with their thread IDs and basic information."""
8788 # Set up logging
8889 if verbose :
8990 logger .setLevel (logging .DEBUG )
9091
92+ logger .info (f"Using checkpoint type: { CHECKPOINT_LABELS .get (checkpoint_type , checkpoint_type )} " )
93+
9194 checkpointer = get_checkpointer (raw_value = checkpoint_type )
9295 if checkpointer is None :
9396 logger .info ("No checkpointing is configured." )
9497 return
95- checkpoints = checkpointer .list (
96- config = None ,
97- )
98- for checkpoint in checkpoints :
99- logger .info (f"Thread ID: { checkpoint .config ['configurable' ].get ('thread_id' )} " )
100- logger .info (f"{ checkpoint .checkpoint ['channel_values' ]} " )
101- messages = checkpoint .checkpoint ["channel_values" ].get ("messages" ) or []
102- for message in messages :
103- if message is not None :
104- logger .info (f" - { message } " )
105- else :
106- logger .info (" - None" )
98+
99+ try :
100+ checkpoints = list (checkpointer .list (config = None ))
101+
102+ if not checkpoints :
103+ logger .info ("No checkpoints found." )
104+ return
105+
106+ logger .info (f"Found { len (checkpoints )} checkpoint(s):" )
107+ logger .info ("-" * 60 )
108+
109+ for i , checkpoint in enumerate (checkpoints , 1 ):
110+ thread_id = checkpoint .config ["configurable" ].get ("thread_id" , "Unknown" )
111+ checkpoint_id = checkpoint .config .get ("checkpoint_id" , "Unknown" )
112+
113+ logger .info (f"{ i } . Thread ID: { thread_id } " )
114+ logger .info (f" Checkpoint ID: { checkpoint_id } " )
115+
116+ # Count messages in this checkpoint
117+ messages = checkpoint .checkpoint ["channel_values" ].get ("messages" ) or []
118+ non_null_messages = [msg for msg in messages if msg is not None ]
119+ logger .info (f" Messages: { len (non_null_messages )} total" )
120+
121+ if verbose and non_null_messages :
122+ logger .info (" Recent messages:" )
123+ # Show last 2 messages for brevity
124+ for msg in non_null_messages [- 2 :]:
125+ if hasattr (msg , "content" ):
126+ content = str (msg .content )
127+ content_preview = content [:100 ] + "..." if len (content ) > 100 else content
128+ msg_type = type (msg ).__name__
129+ logger .info (f" [{ msg_type } ] { content_preview } " )
130+
131+ logger .info ("-" * 60 )
132+
133+ except Exception as e :
134+ logger .error (f"Error listing checkpoints: { str (e )} " )
135+ if verbose :
136+ import traceback
137+
138+ logger .debug (traceback .format_exc ())
107139
108140
109141@app .command ()
110142def list_messages (
143+ thread_id : str = typer .Argument (help = "Thread ID to retrieve messages from" ),
144+ checkpoint_type : str = typer .Option (
145+ DEFAULT_CHECKPOINT_TYPE .value ,
146+ "--type" ,
147+ "-t" ,
148+ case_sensitive = False ,
149+ help = f"Type of checkpoint to use. Options: { ', ' .join ([f'{ key } ({ value } )' for key , value in CHECKPOINT_LABELS .items ()])} . Default is '{ DEFAULT_CHECKPOINT_TYPE .value } '." , # noqa: E501
150+ ),
151+ limit : int = typer .Option (None , "--limit" , "-l" , help = "Maximum number of messages to display (default: all)" ),
111152 verbose : bool = typer .Option (
112153 False ,
113154 "--verbose" ,
114155 "-v" ,
115156 help = "Enable verbose output" ,
116157 ),
117158):
159+ """List messages from a specific checkpoint thread."""
118160 # Set up logging
119161 if verbose :
120162 logger .setLevel (logging .DEBUG )
121163
164+ logger .info (f"Using checkpoint type: { CHECKPOINT_LABELS .get (checkpoint_type , checkpoint_type )} " )
165+ logger .info (f"Retrieving messages for thread ID: { thread_id } " )
166+
167+ checkpointer = get_checkpointer (raw_value = checkpoint_type )
168+ if checkpointer is None :
169+ logger .info ("No checkpointing is configured." )
170+ return
171+
172+ try :
173+ # Search for the specific thread
174+ checkpoints = list (checkpointer .list (config = None ))
175+ target_checkpoint = None
176+
177+ for checkpoint in checkpoints :
178+ if checkpoint .config ["configurable" ].get ("thread_id" ) == thread_id :
179+ target_checkpoint = checkpoint
180+ break
181+
182+ if target_checkpoint is None :
183+ logger .error (f"Thread ID '{ thread_id } ' not found." )
184+ logger .info ("Available thread IDs:" )
185+ for checkpoint in checkpoints :
186+ available_thread_id = checkpoint .config ["configurable" ].get ("thread_id" )
187+ logger .info (f" - { available_thread_id } " )
188+ return
189+
190+ # Extract messages
191+ messages = target_checkpoint .checkpoint ["channel_values" ].get ("messages" ) or []
192+ non_null_messages = [msg for msg in messages if msg is not None ]
193+
194+ if not non_null_messages :
195+ logger .info ("No messages found in this thread." )
196+ return
197+
198+ # Apply limit if specified
199+ if limit and limit > 0 :
200+ if limit < len (non_null_messages ):
201+ logger .info (f"Showing last { limit } of { len (non_null_messages )} messages:" )
202+ non_null_messages = non_null_messages [- limit :]
203+ else :
204+ logger .info (f"Showing all { len (non_null_messages )} messages:" )
205+ else :
206+ logger .info (f"Showing all { len (non_null_messages )} messages:" )
207+
208+ logger .info ("=" * 80 )
209+
210+ for i , msg in enumerate (non_null_messages , 1 ):
211+ msg_type = type (msg ).__name__
212+ logger .info (f"Message { i } [{ msg_type } ]:" )
213+
214+ # Handle different message types
215+ if hasattr (msg , "content" ):
216+ logger .info (f" Content: { msg .content } " )
217+
218+ if hasattr (msg , "role" ):
219+ logger .info (f" Role: { msg .role } " )
220+
221+ if hasattr (msg , "name" ):
222+ logger .info (f" Name: { msg .name } " )
223+
224+ if hasattr (msg , "tool_calls" ) and msg .tool_calls :
225+ logger .info (f" Tool calls: { len (msg .tool_calls )} " )
226+ if verbose :
227+ for j , tool_call in enumerate (msg .tool_calls , 1 ):
228+ logger .info (f" { j } . { tool_call } " )
229+
230+ if hasattr (msg , "additional_kwargs" ) and msg .additional_kwargs and verbose :
231+ logger .info (f" Additional kwargs: { msg .additional_kwargs } " )
232+
233+ # Show raw message in verbose mode
234+ if verbose :
235+ logger .info (f" Raw: { msg } " )
236+
237+ logger .info ("-" * 40 )
238+
239+ logger .info ("=" * 80 )
240+
241+ except Exception as e :
242+ logger .error (f"Error retrieving messages: { str (e )} " )
243+ if verbose :
244+ import traceback
245+
246+ logger .debug (traceback .format_exc ())
247+
122248
123249if __name__ == "__main__" :
124250 load_dotenv (
0 commit comments