youngjin-langchain-tools 0.1.4__py3-none-any.whl → 0.2.1__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.
@@ -124,13 +124,22 @@ def _parse_error(error: Exception) -> Dict[str, Any]:
124
124
 
125
125
  @dataclass
126
126
  class StreamlitLanggraphHandlerConfig:
127
- """Configuration for StreamlitLanggraphHandler."""
127
+ """Configuration for StreamlitLanggraphHandler.
128
+
129
+ Attributes:
130
+ expand_new_thoughts: 도구 결과 expander를 펼친 상태로 표시할지 여부.
131
+ collapse_completed_thoughts: 완료 시 status container를 접을지 여부.
132
+ max_thought_containers: 표시할 최대 사고 과정 수 (초과 시 History로 이동).
133
+ max_tool_content_length: 도구 결과 최대 표시 문자 수.
134
+ show_tool_calls: 도구 호출 정보 표시 여부.
135
+ show_tool_results: 도구 실행 결과 표시 여부.
136
+ """
128
137
 
129
138
  expand_new_thoughts: bool = True
130
- """Whether to expand the status container to show tool calls."""
139
+ """Whether to show tool result expanders in expanded state."""
131
140
 
132
141
  collapse_completed_thoughts: bool = True
133
- """Whether to automatically collapse completed thought containers."""
142
+ """Whether to collapse the status container when agent completes."""
134
143
 
135
144
  max_thought_containers: int = 4
136
145
  """Maximum number of thought containers to show at once.
@@ -256,10 +265,11 @@ class StreamlitLanggraphHandler:
256
265
  self._container = container
257
266
  self._final_response: str = ""
258
267
  self._status_container: Any = None
268
+ self._thoughts_placeholder: Any = None # Placeholder inside status for re-render
259
269
  self._response_placeholder: Any = None
260
- self._thoughts_placeholder: Any = None # Placeholder for thoughts area
261
270
  self._thought_history: List[Dict[str, Any]] = [] # History of old thoughts
262
271
  self._current_thoughts: List[Dict[str, Any]] = [] # Current visible thoughts
272
+ self._thought_counter: int = 0 # Unique ID counter for thoughts
263
273
 
264
274
  @property
265
275
  def config(self) -> StreamlitLanggraphHandlerConfig:
@@ -353,24 +363,26 @@ class StreamlitLanggraphHandler:
353
363
  self._final_response = ""
354
364
  self._thought_history = []
355
365
  self._current_thoughts = []
366
+ self._thought_counter = 0
356
367
 
357
368
  # Create UI components
358
369
  with self._container:
359
- # Thoughts area placeholder (manages history + current thoughts)
360
- self._thoughts_placeholder = st.empty()
361
370
  self._status_container = st.status(
362
371
  self._config.thinking_label,
363
- expanded=self._config.expand_new_thoughts
372
+ expanded=True # Always start expanded during processing
364
373
  )
374
+ # Create placeholder INSIDE status for thought re-rendering
375
+ with self._status_container:
376
+ self._thoughts_placeholder = st.empty()
365
377
  self._response_placeholder = st.empty()
366
378
 
367
379
  # Stream from agent with error handling
368
- config = config or {}
380
+ agent_config = config or {}
369
381
 
370
382
  try:
371
383
  for stream_mode, data in agent.stream(
372
384
  input,
373
- config=config,
385
+ config=agent_config,
374
386
  stream_mode=["messages", "updates"]
375
387
  ):
376
388
  if stream_mode == "updates":
@@ -378,11 +390,11 @@ class StreamlitLanggraphHandler:
378
390
  elif stream_mode == "messages":
379
391
  yield from self._handle_messages(data)
380
392
 
381
- # Mark as complete
393
+ # Mark as complete - collapse based on config
382
394
  self._status_container.update(
383
395
  label=self._config.complete_label,
384
396
  state="complete",
385
- expanded=False
397
+ expanded=not self._config.collapse_completed_thoughts
386
398
  )
387
399
 
388
400
  except Exception as e:
@@ -430,67 +442,88 @@ class StreamlitLanggraphHandler:
430
442
 
431
443
  def _add_thought(self, thought_type: str, data: Dict[str, Any]) -> None:
432
444
  """Add a thought and manage history if max_thought_containers exceeded."""
433
- thought = {"type": thought_type, "data": data}
445
+ self._thought_counter += 1
446
+ thought = {
447
+ "id": self._thought_counter,
448
+ "type": thought_type,
449
+ "data": data
450
+ }
434
451
  self._current_thoughts.append(thought)
435
452
 
436
453
  # Check if we need to move old thoughts to history
437
454
  max_containers = self._config.max_thought_containers
438
- if len(self._current_thoughts) > max_containers:
439
- # Move oldest thoughts to history
440
- while len(self._current_thoughts) > max_containers:
441
- old_thought = self._current_thoughts.pop(0)
442
- self._thought_history.append(old_thought)
455
+ while len(self._current_thoughts) > max_containers:
456
+ old_thought = self._current_thoughts.pop(0)
457
+ self._thought_history.append(old_thought)
443
458
 
444
- # Re-render the entire thoughts area
445
- self._render_thoughts()
459
+ def _render_thought_item(
460
+ self,
461
+ thought: Dict[str, Any],
462
+ st_module: Any,
463
+ in_history: bool = False
464
+ ) -> None:
465
+ """Render a single thought item.
446
466
 
447
- def _render_thought_item(self, thought: Dict[str, Any], in_history: bool = False) -> None:
448
- """Render a single thought item."""
449
- import streamlit as st
467
+ Args:
468
+ thought: The thought dict with id, type, data.
469
+ st_module: The streamlit module reference.
470
+ in_history: Whether this thought is in history section.
471
+ """
472
+ thought_type = thought["type"]
473
+ thought_data = thought["data"]
474
+
475
+ if thought_type == "tool_call":
476
+ # Tool call display: 🔧 tool_name: {args}
477
+ st_module.markdown(
478
+ f"{self._config.tool_call_emoji} "
479
+ f"**{thought_data['name']}**: `{thought_data['args']}`"
480
+ )
481
+
482
+ elif thought_type == "tool_result":
483
+ # Tool result display: ✅ tool_name 완료 + expander
484
+ tool_name = thought_data['name']
485
+ content = thought_data['content']
486
+
487
+ st_module.markdown(
488
+ f"{self._config.tool_complete_emoji} "
489
+ f"**{tool_name}** 완료"
490
+ )
450
491
 
451
- if thought["type"] == "tool_call":
452
- if self._config.show_tool_calls:
453
- st.markdown(
454
- f"{self._config.tool_call_emoji} "
455
- f"**{thought['data']['name']}**: `{thought['data']['args']}`"
456
- )
457
- elif thought["type"] == "tool_result":
458
- if self._config.show_tool_results:
459
- tool_name = thought['data']['name']
460
- content = thought['data']['content']
461
-
462
- st.markdown(
463
- f"{self._config.tool_complete_emoji} "
464
- f"**{tool_name}** 완료"
465
- )
466
-
467
- if in_history:
468
- # Shorter preview in history
469
- max_len = 200
492
+ # Determine max length and expand state
493
+ if in_history:
494
+ max_len = 200
495
+ should_expand = False # History items always collapsed
496
+ else:
497
+ max_len = self._config.max_tool_content_length
498
+ should_expand = self._config.expand_new_thoughts
499
+
500
+ with st_module.expander(f"📋 {tool_name} 결과 보기", expanded=should_expand):
501
+ if len(content) > max_len:
502
+ st_module.code(content[:max_len] + "\n... (truncated)", language="text")
470
503
  else:
471
- max_len = self._config.max_tool_content_length
504
+ st_module.code(content, language="text")
472
505
 
473
- expanded = not self._config.collapse_completed_thoughts
474
- with st.expander(f"📋 {tool_name} 결과 보기", expanded=expanded if not in_history else False):
475
- if len(content) > max_len:
476
- st.code(content[:max_len] + "\n... (truncated)", language="text")
477
- else:
478
- st.code(content, language="text")
506
+ def _render_thoughts_in_status(self) -> None:
507
+ """Render all thoughts (history + current) inside the status container.
479
508
 
480
- def _render_thoughts(self) -> None:
481
- """Render all thoughts (history + current) in the placeholder."""
509
+ Uses placeholder.container() to completely replace previous content.
510
+ """
482
511
  import streamlit as st
483
512
 
513
+ # Clear and re-render using placeholder
484
514
  with self._thoughts_placeholder.container():
485
515
  # Render history expander if there are old thoughts
486
516
  if self._thought_history:
487
- with st.expander(f"📜 History ({len(self._thought_history)} items)", expanded=False):
517
+ with st.expander(
518
+ f"📜 History ({len(self._thought_history)} items)",
519
+ expanded=False
520
+ ):
488
521
  for thought in self._thought_history:
489
- self._render_thought_item(thought, in_history=True)
522
+ self._render_thought_item(thought, st, in_history=True)
490
523
 
491
524
  # Render current thoughts
492
525
  for thought in self._current_thoughts:
493
- self._render_thought_item(thought, in_history=False)
526
+ self._render_thought_item(thought, st, in_history=False)
494
527
 
495
528
  def _handle_updates(
496
529
  self,
@@ -509,8 +542,13 @@ class StreamlitLanggraphHandler:
509
542
  tool_name = tc.get('name', 'tool')
510
543
  tool_args = tc.get('args', {})
511
544
 
512
- # Add to thought tracking (this triggers re-render)
513
- self._add_thought("tool_call", {"name": tool_name, "args": tool_args})
545
+ # Only add to thoughts if show_tool_calls is True
546
+ if self._config.show_tool_calls:
547
+ self._add_thought(
548
+ "tool_call",
549
+ {"name": tool_name, "args": tool_args}
550
+ )
551
+ self._render_thoughts_in_status()
514
552
 
515
553
  # Update status label to show current action
516
554
  self._status_container.update(
@@ -528,8 +566,13 @@ class StreamlitLanggraphHandler:
528
566
  tool_name = msg.name
529
567
  tool_content = str(msg.content) if hasattr(msg, 'content') else ""
530
568
 
531
- # Add to thought tracking (this triggers re-render)
532
- self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
569
+ # Only add to thoughts if show_tool_results is True
570
+ if self._config.show_tool_results:
571
+ self._add_thought(
572
+ "tool_result",
573
+ {"name": tool_name, "content": tool_content}
574
+ )
575
+ self._render_thoughts_in_status()
533
576
 
534
577
  # Update status to show thinking again
535
578
  self._status_container.update(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: youngjin-langchain-tools
3
- Version: 0.1.4
3
+ Version: 0.2.1
4
4
  Summary: LangGraph utilities for Streamlit - StreamlitLanggraphHandler and more
5
5
  Project-URL: Homepage, https://github.com/yourusername/youngjin-langchain-tools
6
6
  Project-URL: Documentation, https://github.com/yourusername/youngjin-langchain-tools#readme
@@ -1,9 +1,9 @@
1
1
  youngjin_langchain_tools/__init__.py,sha256=S5GJtbYymhDuGtTibEG61Li9UvwvoFtnKm4mxjWNgnU,1310
2
2
  youngjin_langchain_tools/handlers/__init__.py,sha256=-vGk-m1fqOipJSe02ogcScE0K3pdVwO9A_EqBovPRxo,397
3
- youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py,sha256=5CvUm2kN9peE3v3dqboqfXMVtQ2EthwOaJkO_T2l5J8,21390
3
+ youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py,sha256=GA2A7aYtcNnZ--XBcV2T1mzm1toGdALIPyNCxKIcU40,23009
4
4
  youngjin_langchain_tools/utils/__init__.py,sha256=LgSd7Gz2n5WgIhxaKHqpmQVUaBUDT_TTTw63hzoWGgM,272
5
5
  youngjin_langchain_tools/utils/config.py,sha256=hHvdxsn5ZFWRJvR6N7ODy94JbCkeJocsS58hmvxoK5I,1640
6
- youngjin_langchain_tools-0.1.4.dist-info/METADATA,sha256=MDDYlAZ4SjXGtWT1YQl3JB8Gt66mnDmy1BdaizzhIDA,6959
7
- youngjin_langchain_tools-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
- youngjin_langchain_tools-0.1.4.dist-info/licenses/LICENSE,sha256=fENUlkDDxJEn5c0u8mhVcz5ek-rTg2L4Kby1tMNchI8,11342
9
- youngjin_langchain_tools-0.1.4.dist-info/RECORD,,
6
+ youngjin_langchain_tools-0.2.1.dist-info/METADATA,sha256=aTH3J-FR4eRLgdaQ7OZXb8CCrH0bazeinvz42Sl_rK4,6959
7
+ youngjin_langchain_tools-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ youngjin_langchain_tools-0.2.1.dist-info/licenses/LICENSE,sha256=fENUlkDDxJEn5c0u8mhVcz5ek-rTg2L4Kby1tMNchI8,11342
9
+ youngjin_langchain_tools-0.2.1.dist-info/RECORD,,