youngjin-langchain-tools 0.1.3__tar.gz → 0.1.4__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: youngjin-langchain-tools
3
- Version: 0.1.3
3
+ Version: 0.1.4
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "youngjin-langchain-tools"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "LangGraph utilities for Streamlit - StreamlitLanggraphHandler and more"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -257,7 +257,7 @@ class StreamlitLanggraphHandler:
257
257
  self._final_response: str = ""
258
258
  self._status_container: Any = None
259
259
  self._response_placeholder: Any = None
260
- self._history_placeholder: Any = None # Placeholder for history expander
260
+ self._thoughts_placeholder: Any = None # Placeholder for thoughts area
261
261
  self._thought_history: List[Dict[str, Any]] = [] # History of old thoughts
262
262
  self._current_thoughts: List[Dict[str, Any]] = [] # Current visible thoughts
263
263
 
@@ -356,8 +356,8 @@ class StreamlitLanggraphHandler:
356
356
 
357
357
  # Create UI components
358
358
  with self._container:
359
- # History expander placeholder (will be shown when needed)
360
- self._history_placeholder = st.empty()
359
+ # Thoughts area placeholder (manages history + current thoughts)
360
+ self._thoughts_placeholder = st.empty()
361
361
  self._status_container = st.status(
362
362
  self._config.thinking_label,
363
363
  expanded=self._config.expand_new_thoughts
@@ -430,8 +430,6 @@ class StreamlitLanggraphHandler:
430
430
 
431
431
  def _add_thought(self, thought_type: str, data: Dict[str, Any]) -> None:
432
432
  """Add a thought and manage history if max_thought_containers exceeded."""
433
- import streamlit as st
434
-
435
433
  thought = {"type": thought_type, "data": data}
436
434
  self._current_thoughts.append(thought)
437
435
 
@@ -443,45 +441,62 @@ class StreamlitLanggraphHandler:
443
441
  old_thought = self._current_thoughts.pop(0)
444
442
  self._thought_history.append(old_thought)
445
443
 
446
- # Update history expander
447
- self._update_history_expander()
444
+ # Re-render the entire thoughts area
445
+ self._render_thoughts()
448
446
 
449
- def _update_history_expander(self) -> None:
450
- """Update the history expander with old thoughts."""
447
+ def _render_thought_item(self, thought: Dict[str, Any], in_history: bool = False) -> None:
448
+ """Render a single thought item."""
451
449
  import streamlit as st
452
450
 
453
- if not self._thought_history:
454
- return
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
472
+
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")
479
+
480
+ def _render_thoughts(self) -> None:
481
+ """Render all thoughts (history + current) in the placeholder."""
482
+ import streamlit as st
455
483
 
456
- with self._history_placeholder.container():
457
- with st.expander(f"📜 History ({len(self._thought_history)} items)", expanded=False):
458
- for thought in self._thought_history:
459
- if thought["type"] == "tool_call":
460
- st.markdown(
461
- f"{self._config.tool_call_emoji} "
462
- f"**{thought['data']['name']}**: `{thought['data']['args']}`"
463
- )
464
- elif thought["type"] == "tool_result":
465
- st.markdown(
466
- f"{self._config.tool_complete_emoji} "
467
- f"**{thought['data']['name']}** 완료"
468
- )
469
- content = thought['data']['content']
470
- if len(content) > 200: # Shorter preview in history
471
- st.code(content[:200] + "\n... (truncated)", language="text")
472
- else:
473
- st.code(content, language="text")
484
+ with self._thoughts_placeholder.container():
485
+ # Render history expander if there are old thoughts
486
+ if self._thought_history:
487
+ with st.expander(f"📜 History ({len(self._thought_history)} items)", expanded=False):
488
+ for thought in self._thought_history:
489
+ self._render_thought_item(thought, in_history=True)
490
+
491
+ # Render current thoughts
492
+ for thought in self._current_thoughts:
493
+ self._render_thought_item(thought, in_history=False)
474
494
 
475
495
  def _handle_updates(
476
496
  self,
477
497
  data: Dict[str, Any]
478
498
  ) -> Generator[Dict[str, Any], None, None]:
479
499
  """Handle 'updates' stream mode events."""
480
- try:
481
- import streamlit as st
482
- except ImportError:
483
- return
484
-
485
500
  for source, update in data.items():
486
501
  if not isinstance(update, dict):
487
502
  continue
@@ -490,55 +505,42 @@ class StreamlitLanggraphHandler:
490
505
  for msg in messages:
491
506
  # Handle tool calls
492
507
  if hasattr(msg, 'tool_calls') and msg.tool_calls:
493
- if self._config.show_tool_calls:
494
- for tc in msg.tool_calls:
495
- tool_name = tc.get('name', 'tool')
496
- tool_args = tc.get('args', {})
508
+ for tc in msg.tool_calls:
509
+ tool_name = tc.get('name', 'tool')
510
+ tool_args = tc.get('args', {})
497
511
 
498
- # Add to thought tracking
499
- self._add_thought("tool_call", {"name": tool_name, "args": tool_args})
512
+ # Add to thought tracking (this triggers re-render)
513
+ self._add_thought("tool_call", {"name": tool_name, "args": tool_args})
500
514
 
501
- with self._status_container:
502
- st.write(
503
- f"{self._config.tool_call_emoji} "
504
- f"**{tool_name}**: `{tool_args}`"
505
- )
515
+ # Update status label to show current action
516
+ self._status_container.update(
517
+ label=f"{self._config.tool_call_emoji} {tool_name}...",
518
+ state="running"
519
+ )
506
520
 
507
- yield {
508
- "type": "tool_call",
509
- "data": {"name": tool_name, "args": tool_args}
510
- }
521
+ yield {
522
+ "type": "tool_call",
523
+ "data": {"name": tool_name, "args": tool_args}
524
+ }
511
525
 
512
526
  # Handle tool results
513
527
  if source == "tools" and hasattr(msg, 'name'):
514
- if self._config.show_tool_results:
515
- tool_name = msg.name
516
- tool_content = str(msg.content) if hasattr(msg, 'content') else ""
517
-
518
- # Add to thought tracking
519
- self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
520
-
521
- with self._status_container:
522
- st.write(
523
- f"{self._config.tool_complete_emoji} "
524
- f"**{tool_name}** 완료"
525
- )
526
- # Collapse based on config
527
- expanded = not self._config.collapse_completed_thoughts
528
- with st.expander(f"📋 {tool_name} 결과 보기", expanded=expanded):
529
- if len(tool_content) > self._config.max_tool_content_length:
530
- st.code(
531
- tool_content[:self._config.max_tool_content_length]
532
- + "\n... (truncated)",
533
- language="text"
534
- )
535
- else:
536
- st.code(tool_content, language="text")
537
-
538
- yield {
539
- "type": "tool_result",
540
- "data": {"name": tool_name, "content": tool_content}
541
- }
528
+ tool_name = msg.name
529
+ tool_content = str(msg.content) if hasattr(msg, 'content') else ""
530
+
531
+ # Add to thought tracking (this triggers re-render)
532
+ self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
533
+
534
+ # Update status to show thinking again
535
+ self._status_container.update(
536
+ label=self._config.thinking_label,
537
+ state="running"
538
+ )
539
+
540
+ yield {
541
+ "type": "tool_result",
542
+ "data": {"name": tool_name, "content": tool_content}
543
+ }
542
544
 
543
545
  def _handle_messages(
544
546
  self,