youngjin-langchain-tools 0.1.4__tar.gz → 0.2.1__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.
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/PKG-INFO +1 -1
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/pyproject.toml +1 -1
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py +100 -57
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/.gitignore +0 -0
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/LICENSE +0 -0
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/README.md +0 -0
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/youngjin_langchain_tools/__init__.py +0 -0
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/youngjin_langchain_tools/handlers/__init__.py +0 -0
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/youngjin_langchain_tools/utils/__init__.py +0 -0
- {youngjin_langchain_tools-0.1.4 → youngjin_langchain_tools-0.2.1}/youngjin_langchain_tools/utils/config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youngjin-langchain-tools
|
|
3
|
-
Version: 0.1
|
|
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
|
|
@@ -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
|
|
139
|
+
"""Whether to show tool result expanders in expanded state."""
|
|
131
140
|
|
|
132
141
|
collapse_completed_thoughts: bool = True
|
|
133
|
-
"""Whether to
|
|
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=
|
|
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
|
-
|
|
380
|
+
agent_config = config or {}
|
|
369
381
|
|
|
370
382
|
try:
|
|
371
383
|
for stream_mode, data in agent.stream(
|
|
372
384
|
input,
|
|
373
|
-
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=
|
|
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
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
|
|
445
|
-
self
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
452
|
-
if
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
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
|
-
|
|
504
|
+
st_module.code(content, language="text")
|
|
472
505
|
|
|
473
|
-
|
|
474
|
-
|
|
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
|
-
|
|
481
|
-
"""
|
|
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(
|
|
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
|
-
#
|
|
513
|
-
self.
|
|
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
|
-
#
|
|
532
|
-
self.
|
|
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(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|