youngjin-langchain-tools 0.1.4__py3-none-any.whl → 0.2.0__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.
@@ -257,9 +266,9 @@ class StreamlitLanggraphHandler:
257
266
  self._final_response: str = ""
258
267
  self._status_container: Any = None
259
268
  self._response_placeholder: Any = None
260
- self._thoughts_placeholder: Any = None # Placeholder for thoughts area
261
269
  self._thought_history: List[Dict[str, Any]] = [] # History of old thoughts
262
270
  self._current_thoughts: List[Dict[str, Any]] = [] # Current visible thoughts
271
+ self._thought_counter: int = 0 # Unique ID counter for thoughts
263
272
 
264
273
  @property
265
274
  def config(self) -> StreamlitLanggraphHandlerConfig:
@@ -353,24 +362,23 @@ class StreamlitLanggraphHandler:
353
362
  self._final_response = ""
354
363
  self._thought_history = []
355
364
  self._current_thoughts = []
365
+ self._thought_counter = 0
356
366
 
357
367
  # Create UI components
358
368
  with self._container:
359
- # Thoughts area placeholder (manages history + current thoughts)
360
- self._thoughts_placeholder = st.empty()
361
369
  self._status_container = st.status(
362
370
  self._config.thinking_label,
363
- expanded=self._config.expand_new_thoughts
371
+ expanded=True # Always start expanded during processing
364
372
  )
365
373
  self._response_placeholder = st.empty()
366
374
 
367
375
  # Stream from agent with error handling
368
- config = config or {}
376
+ agent_config = config or {}
369
377
 
370
378
  try:
371
379
  for stream_mode, data in agent.stream(
372
380
  input,
373
- config=config,
381
+ config=agent_config,
374
382
  stream_mode=["messages", "updates"]
375
383
  ):
376
384
  if stream_mode == "updates":
@@ -378,11 +386,11 @@ class StreamlitLanggraphHandler:
378
386
  elif stream_mode == "messages":
379
387
  yield from self._handle_messages(data)
380
388
 
381
- # Mark as complete
389
+ # Mark as complete - collapse based on config
382
390
  self._status_container.update(
383
391
  label=self._config.complete_label,
384
392
  state="complete",
385
- expanded=False
393
+ expanded=not self._config.collapse_completed_thoughts
386
394
  )
387
395
 
388
396
  except Exception as e:
@@ -430,67 +438,84 @@ class StreamlitLanggraphHandler:
430
438
 
431
439
  def _add_thought(self, thought_type: str, data: Dict[str, Any]) -> None:
432
440
  """Add a thought and manage history if max_thought_containers exceeded."""
433
- thought = {"type": thought_type, "data": data}
441
+ self._thought_counter += 1
442
+ thought = {
443
+ "id": self._thought_counter,
444
+ "type": thought_type,
445
+ "data": data
446
+ }
434
447
  self._current_thoughts.append(thought)
435
448
 
436
449
  # Check if we need to move old thoughts to history
437
450
  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)
451
+ while len(self._current_thoughts) > max_containers:
452
+ old_thought = self._current_thoughts.pop(0)
453
+ self._thought_history.append(old_thought)
443
454
 
444
- # Re-render the entire thoughts area
445
- self._render_thoughts()
455
+ def _render_thought_item(
456
+ self,
457
+ thought: Dict[str, Any],
458
+ st_module: Any,
459
+ in_history: bool = False
460
+ ) -> None:
461
+ """Render a single thought item.
446
462
 
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
463
+ Args:
464
+ thought: The thought dict with id, type, data.
465
+ st_module: The streamlit module reference.
466
+ in_history: Whether this thought is in history section.
467
+ """
468
+ thought_type = thought["type"]
469
+ thought_data = thought["data"]
470
+
471
+ if thought_type == "tool_call":
472
+ # Tool call display: 🔧 tool_name: {args}
473
+ st_module.markdown(
474
+ f"{self._config.tool_call_emoji} "
475
+ f"**{thought_data['name']}**: `{thought_data['args']}`"
476
+ )
450
477
 
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
470
- else:
471
- max_len = self._config.max_tool_content_length
478
+ elif thought_type == "tool_result":
479
+ # Tool result display: ✅ tool_name 완료 + expander
480
+ tool_name = thought_data['name']
481
+ content = thought_data['content']
472
482
 
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")
483
+ st_module.markdown(
484
+ f"{self._config.tool_complete_emoji} "
485
+ f"**{tool_name}** 완료"
486
+ )
479
487
 
480
- def _render_thoughts(self) -> None:
481
- """Render all thoughts (history + current) in the placeholder."""
488
+ # Determine max length and expand state
489
+ if in_history:
490
+ max_len = 200
491
+ should_expand = False # History items always collapsed
492
+ else:
493
+ max_len = self._config.max_tool_content_length
494
+ should_expand = self._config.expand_new_thoughts
495
+
496
+ with st_module.expander(f"📋 {tool_name} 결과 보기", expanded=should_expand):
497
+ if len(content) > max_len:
498
+ st_module.code(content[:max_len] + "\n... (truncated)", language="text")
499
+ else:
500
+ st_module.code(content, language="text")
501
+
502
+ def _render_thoughts_in_status(self) -> None:
503
+ """Render all thoughts (history + current) inside the status container."""
482
504
  import streamlit as st
483
505
 
484
- with self._thoughts_placeholder.container():
506
+ with self._status_container:
485
507
  # Render history expander if there are old thoughts
486
508
  if self._thought_history:
487
- with st.expander(f"📜 History ({len(self._thought_history)} items)", expanded=False):
509
+ with st.expander(
510
+ f"📜 History ({len(self._thought_history)} items)",
511
+ expanded=False
512
+ ):
488
513
  for thought in self._thought_history:
489
- self._render_thought_item(thought, in_history=True)
514
+ self._render_thought_item(thought, st, in_history=True)
490
515
 
491
516
  # Render current thoughts
492
517
  for thought in self._current_thoughts:
493
- self._render_thought_item(thought, in_history=False)
518
+ self._render_thought_item(thought, st, in_history=False)
494
519
 
495
520
  def _handle_updates(
496
521
  self,
@@ -509,8 +534,13 @@ class StreamlitLanggraphHandler:
509
534
  tool_name = tc.get('name', 'tool')
510
535
  tool_args = tc.get('args', {})
511
536
 
512
- # Add to thought tracking (this triggers re-render)
513
- self._add_thought("tool_call", {"name": tool_name, "args": tool_args})
537
+ # Only add to thoughts if show_tool_calls is True
538
+ if self._config.show_tool_calls:
539
+ self._add_thought(
540
+ "tool_call",
541
+ {"name": tool_name, "args": tool_args}
542
+ )
543
+ self._render_thoughts_in_status()
514
544
 
515
545
  # Update status label to show current action
516
546
  self._status_container.update(
@@ -528,8 +558,13 @@ class StreamlitLanggraphHandler:
528
558
  tool_name = msg.name
529
559
  tool_content = str(msg.content) if hasattr(msg, 'content') else ""
530
560
 
531
- # Add to thought tracking (this triggers re-render)
532
- self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
561
+ # Only add to thoughts if show_tool_results is True
562
+ if self._config.show_tool_results:
563
+ self._add_thought(
564
+ "tool_result",
565
+ {"name": tool_name, "content": tool_content}
566
+ )
567
+ self._render_thoughts_in_status()
533
568
 
534
569
  # Update status to show thinking again
535
570
  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.0
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=O2_B5I7E0OWqh1JdS014aGw_smQsCpT7NykxcH16Ql8,22599
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.0.dist-info/METADATA,sha256=3zKlWZoqhkSKRxhFV89VL08pgwtfYxZgORGKWZM3N4Q,6959
7
+ youngjin_langchain_tools-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ youngjin_langchain_tools-0.2.0.dist-info/licenses/LICENSE,sha256=fENUlkDDxJEn5c0u8mhVcz5ek-rTg2L4Kby1tMNchI8,11342
9
+ youngjin_langchain_tools-0.2.0.dist-info/RECORD,,