Skip to content

Commit 3580781

Browse files
committed
gen: あなたは google の staff software engineer です。LangGraph の checkpoint インタフェースから checkpoint を列挙する list_checkpoint、checkpoint を指定してメッセージを出力する list_message サブコマンドを #file:checkpoint_operator.py に実装してください。
1 parent 56cf475 commit 3580781

File tree

1 file changed

+138
-12
lines changed

1 file changed

+138
-12
lines changed

scripts/checkpoint_operator.py

Lines changed: 138 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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()
110142
def 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

123249
if __name__ == "__main__":
124250
load_dotenv(

0 commit comments

Comments
 (0)