youngjin-langchain-tools 0.1.1__py3-none-any.whl → 0.1.3__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.
- youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py +93 -29
- {youngjin_langchain_tools-0.1.1.dist-info → youngjin_langchain_tools-0.1.3.dist-info}/METADATA +6 -5
- {youngjin_langchain_tools-0.1.1.dist-info → youngjin_langchain_tools-0.1.3.dist-info}/RECORD +5 -5
- {youngjin_langchain_tools-0.1.1.dist-info → youngjin_langchain_tools-0.1.3.dist-info}/WHEEL +0 -0
- {youngjin_langchain_tools-0.1.1.dist-info → youngjin_langchain_tools-0.1.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,8 +8,8 @@ LangGraph agent responses in Streamlit applications.
|
|
|
8
8
|
Replaces the deprecated StreamlitCallbackHandler for LangGraph-based agents.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
from typing import Any, Dict, List, Optional,
|
|
12
|
-
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Dict, List, Optional, Generator
|
|
12
|
+
from dataclasses import dataclass
|
|
13
13
|
import logging
|
|
14
14
|
import re
|
|
15
15
|
|
|
@@ -22,38 +22,33 @@ logger = logging.getLogger(__name__)
|
|
|
22
22
|
# ============================================================
|
|
23
23
|
ERROR_PATTERNS = {
|
|
24
24
|
# OpenAI errors
|
|
25
|
-
r"AuthenticationError.*API key": {
|
|
26
|
-
"title": "🔑 API Key 오류",
|
|
27
|
-
"message": "API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
25
|
+
r"AuthenticationError.*API key|openai.*api.*key|OPENAI_API_KEY": {
|
|
26
|
+
"title": "🔑 OpenAI API Key 오류",
|
|
27
|
+
"message": "OpenAI API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
28
28
|
"solution": [
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"3. API 키는 https://platform.openai.com/api-keys 에서 발급받을 수 있습니다.",
|
|
29
|
+
"환경변수 `OPENAI_API_KEY`를 설정하거나 클라이언트 초기화 시 `api_key` 파라미터를 전달하세요.",
|
|
30
|
+
"API 키 발급: https://platform.openai.com/api-keys",
|
|
32
31
|
],
|
|
33
32
|
},
|
|
34
33
|
r"RateLimitError|rate_limit|429": {
|
|
35
34
|
"title": "⏱️ Rate Limit 초과",
|
|
36
35
|
"message": "API 요청 한도를 초과했습니다.",
|
|
37
36
|
"solution": [
|
|
38
|
-
"
|
|
39
|
-
"2. API 사용량을 확인하세요: https://platform.openai.com/usage",
|
|
40
|
-
"3. 필요시 요금제를 업그레이드하세요.",
|
|
37
|
+
"잠시 후 다시 시도하거나, API 사용량 및 요금제를 확인하세요.",
|
|
41
38
|
],
|
|
42
39
|
},
|
|
43
40
|
r"InsufficientQuotaError|insufficient_quota|billing": {
|
|
44
41
|
"title": "💳 크레딧 부족",
|
|
45
42
|
"message": "API 크레딧이 부족합니다.",
|
|
46
43
|
"solution": [
|
|
47
|
-
"
|
|
48
|
-
"2. 크레딧을 충전하세요.",
|
|
44
|
+
"API 제공자의 결제 페이지에서 크레딧을 충전하세요.",
|
|
49
45
|
],
|
|
50
46
|
},
|
|
51
47
|
r"InvalidRequestError|invalid_request": {
|
|
52
48
|
"title": "❌ 잘못된 요청",
|
|
53
49
|
"message": "API 요청 형식이 올바르지 않습니다.",
|
|
54
50
|
"solution": [
|
|
55
|
-
"
|
|
56
|
-
"2. 모델명이 올바른지 확인하세요.",
|
|
51
|
+
"입력 데이터와 모델명이 올바른지 확인하세요.",
|
|
57
52
|
],
|
|
58
53
|
},
|
|
59
54
|
# Anthropic errors
|
|
@@ -61,8 +56,8 @@ ERROR_PATTERNS = {
|
|
|
61
56
|
"title": "🔑 Anthropic API Key 오류",
|
|
62
57
|
"message": "Anthropic API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
63
58
|
"solution": [
|
|
64
|
-
"
|
|
65
|
-
"
|
|
59
|
+
"환경변수 `ANTHROPIC_API_KEY`를 설정하세요.",
|
|
60
|
+
"API 키 발급: https://console.anthropic.com/",
|
|
66
61
|
],
|
|
67
62
|
},
|
|
68
63
|
# Google errors
|
|
@@ -70,8 +65,8 @@ ERROR_PATTERNS = {
|
|
|
70
65
|
"title": "🔑 Google API Key 오류",
|
|
71
66
|
"message": "Google API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
72
67
|
"solution": [
|
|
73
|
-
"
|
|
74
|
-
"
|
|
68
|
+
"환경변수 `GOOGLE_API_KEY`를 설정하세요.",
|
|
69
|
+
"API 키 발급: https://aistudio.google.com/apikey",
|
|
75
70
|
],
|
|
76
71
|
},
|
|
77
72
|
# Network errors
|
|
@@ -79,18 +74,14 @@ ERROR_PATTERNS = {
|
|
|
79
74
|
"title": "🌐 네트워크 오류",
|
|
80
75
|
"message": "API 서버에 연결할 수 없습니다.",
|
|
81
76
|
"solution": [
|
|
82
|
-
"
|
|
83
|
-
"2. 방화벽/프록시 설정을 확인하세요.",
|
|
84
|
-
"3. API 서버 상태를 확인하세요.",
|
|
77
|
+
"인터넷 연결 및 방화벽/프록시 설정을 확인하세요.",
|
|
85
78
|
],
|
|
86
79
|
},
|
|
87
80
|
r"TimeoutError|timeout|timed out": {
|
|
88
81
|
"title": "⏰ 시간 초과",
|
|
89
82
|
"message": "API 요청이 시간 초과되었습니다.",
|
|
90
83
|
"solution": [
|
|
91
|
-
"
|
|
92
|
-
"2. 잠시 후 다시 시도해주세요.",
|
|
93
|
-
"3. 요청 크기를 줄여보세요.",
|
|
84
|
+
"네트워크 연결을 확인하고 잠시 후 다시 시도하세요.",
|
|
94
85
|
],
|
|
95
86
|
},
|
|
96
87
|
# Model errors
|
|
@@ -98,9 +89,7 @@ ERROR_PATTERNS = {
|
|
|
98
89
|
"title": "🤖 모델 오류",
|
|
99
90
|
"message": "지정된 모델을 찾을 수 없습니다.",
|
|
100
91
|
"solution": [
|
|
101
|
-
"
|
|
102
|
-
"2. 해당 모델에 대한 접근 권한이 있는지 확인하세요.",
|
|
103
|
-
"3. 사용 가능한 모델 목록을 확인하세요.",
|
|
92
|
+
"모델명과 접근 권한을 확인하세요.",
|
|
104
93
|
],
|
|
105
94
|
},
|
|
106
95
|
}
|
|
@@ -140,6 +129,13 @@ class StreamlitLanggraphHandlerConfig:
|
|
|
140
129
|
expand_new_thoughts: bool = True
|
|
141
130
|
"""Whether to expand the status container to show tool calls."""
|
|
142
131
|
|
|
132
|
+
collapse_completed_thoughts: bool = True
|
|
133
|
+
"""Whether to automatically collapse completed thought containers."""
|
|
134
|
+
|
|
135
|
+
max_thought_containers: int = 4
|
|
136
|
+
"""Maximum number of thought containers to show at once.
|
|
137
|
+
When exceeded, oldest thoughts are moved to a 'History' expander."""
|
|
138
|
+
|
|
143
139
|
max_tool_content_length: int = 2000
|
|
144
140
|
"""Maximum length of tool output to display before truncating."""
|
|
145
141
|
|
|
@@ -213,6 +209,8 @@ class StreamlitLanggraphHandler:
|
|
|
213
209
|
container: Any,
|
|
214
210
|
*,
|
|
215
211
|
expand_new_thoughts: bool = True,
|
|
212
|
+
collapse_completed_thoughts: bool = True,
|
|
213
|
+
max_thought_containers: int = 4,
|
|
216
214
|
max_tool_content_length: int = 2000,
|
|
217
215
|
show_tool_calls: bool = True,
|
|
218
216
|
show_tool_results: bool = True,
|
|
@@ -228,6 +226,11 @@ class StreamlitLanggraphHandler:
|
|
|
228
226
|
Usually st.container() or similar.
|
|
229
227
|
expand_new_thoughts: Whether to expand status container
|
|
230
228
|
to show tool calls. Defaults to True.
|
|
229
|
+
collapse_completed_thoughts: Whether to collapse completed
|
|
230
|
+
thought containers. Defaults to True.
|
|
231
|
+
max_thought_containers: Maximum number of thought containers
|
|
232
|
+
to show at once. Oldest are moved to
|
|
233
|
+
'History' expander. Defaults to 4.
|
|
231
234
|
max_tool_content_length: Maximum characters of tool output
|
|
232
235
|
to display. Defaults to 2000.
|
|
233
236
|
show_tool_calls: Whether to show tool call info. Defaults to True.
|
|
@@ -241,6 +244,8 @@ class StreamlitLanggraphHandler:
|
|
|
241
244
|
else:
|
|
242
245
|
self._config = StreamlitLanggraphHandlerConfig(
|
|
243
246
|
expand_new_thoughts=expand_new_thoughts,
|
|
247
|
+
collapse_completed_thoughts=collapse_completed_thoughts,
|
|
248
|
+
max_thought_containers=max_thought_containers,
|
|
244
249
|
max_tool_content_length=max_tool_content_length,
|
|
245
250
|
show_tool_calls=show_tool_calls,
|
|
246
251
|
show_tool_results=show_tool_results,
|
|
@@ -252,6 +257,9 @@ class StreamlitLanggraphHandler:
|
|
|
252
257
|
self._final_response: str = ""
|
|
253
258
|
self._status_container: Any = None
|
|
254
259
|
self._response_placeholder: Any = None
|
|
260
|
+
self._history_placeholder: Any = None # Placeholder for history expander
|
|
261
|
+
self._thought_history: List[Dict[str, Any]] = [] # History of old thoughts
|
|
262
|
+
self._current_thoughts: List[Dict[str, Any]] = [] # Current visible thoughts
|
|
255
263
|
|
|
256
264
|
@property
|
|
257
265
|
def config(self) -> StreamlitLanggraphHandlerConfig:
|
|
@@ -343,9 +351,13 @@ class StreamlitLanggraphHandler:
|
|
|
343
351
|
|
|
344
352
|
# Reset state
|
|
345
353
|
self._final_response = ""
|
|
354
|
+
self._thought_history = []
|
|
355
|
+
self._current_thoughts = []
|
|
346
356
|
|
|
347
357
|
# Create UI components
|
|
348
358
|
with self._container:
|
|
359
|
+
# History expander placeholder (will be shown when needed)
|
|
360
|
+
self._history_placeholder = st.empty()
|
|
349
361
|
self._status_container = st.status(
|
|
350
362
|
self._config.thinking_label,
|
|
351
363
|
expanded=self._config.expand_new_thoughts
|
|
@@ -416,6 +428,50 @@ class StreamlitLanggraphHandler:
|
|
|
416
428
|
|
|
417
429
|
yield {"type": "complete", "data": {"response": self._final_response}}
|
|
418
430
|
|
|
431
|
+
def _add_thought(self, thought_type: str, data: Dict[str, Any]) -> None:
|
|
432
|
+
"""Add a thought and manage history if max_thought_containers exceeded."""
|
|
433
|
+
import streamlit as st
|
|
434
|
+
|
|
435
|
+
thought = {"type": thought_type, "data": data}
|
|
436
|
+
self._current_thoughts.append(thought)
|
|
437
|
+
|
|
438
|
+
# Check if we need to move old thoughts to history
|
|
439
|
+
max_containers = self._config.max_thought_containers
|
|
440
|
+
if len(self._current_thoughts) > max_containers:
|
|
441
|
+
# Move oldest thoughts to history
|
|
442
|
+
while len(self._current_thoughts) > max_containers:
|
|
443
|
+
old_thought = self._current_thoughts.pop(0)
|
|
444
|
+
self._thought_history.append(old_thought)
|
|
445
|
+
|
|
446
|
+
# Update history expander
|
|
447
|
+
self._update_history_expander()
|
|
448
|
+
|
|
449
|
+
def _update_history_expander(self) -> None:
|
|
450
|
+
"""Update the history expander with old thoughts."""
|
|
451
|
+
import streamlit as st
|
|
452
|
+
|
|
453
|
+
if not self._thought_history:
|
|
454
|
+
return
|
|
455
|
+
|
|
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")
|
|
474
|
+
|
|
419
475
|
def _handle_updates(
|
|
420
476
|
self,
|
|
421
477
|
data: Dict[str, Any]
|
|
@@ -439,6 +495,9 @@ class StreamlitLanggraphHandler:
|
|
|
439
495
|
tool_name = tc.get('name', 'tool')
|
|
440
496
|
tool_args = tc.get('args', {})
|
|
441
497
|
|
|
498
|
+
# Add to thought tracking
|
|
499
|
+
self._add_thought("tool_call", {"name": tool_name, "args": tool_args})
|
|
500
|
+
|
|
442
501
|
with self._status_container:
|
|
443
502
|
st.write(
|
|
444
503
|
f"{self._config.tool_call_emoji} "
|
|
@@ -456,12 +515,17 @@ class StreamlitLanggraphHandler:
|
|
|
456
515
|
tool_name = msg.name
|
|
457
516
|
tool_content = str(msg.content) if hasattr(msg, 'content') else ""
|
|
458
517
|
|
|
518
|
+
# Add to thought tracking
|
|
519
|
+
self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
|
|
520
|
+
|
|
459
521
|
with self._status_container:
|
|
460
522
|
st.write(
|
|
461
523
|
f"{self._config.tool_complete_emoji} "
|
|
462
524
|
f"**{tool_name}** 완료"
|
|
463
525
|
)
|
|
464
|
-
|
|
526
|
+
# Collapse based on config
|
|
527
|
+
expanded = not self._config.collapse_completed_thoughts
|
|
528
|
+
with st.expander(f"📋 {tool_name} 결과 보기", expanded=expanded):
|
|
465
529
|
if len(tool_content) > self._config.max_tool_content_length:
|
|
466
530
|
st.code(
|
|
467
531
|
tool_content[:self._config.max_tool_content_length]
|
{youngjin_langchain_tools-0.1.1.dist-info → youngjin_langchain_tools-0.1.3.dist-info}/METADATA
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youngjin-langchain-tools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
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
|
|
7
7
|
Project-URL: Repository, https://github.com/yourusername/youngjin-langchain-tools.git
|
|
8
8
|
Project-URL: Issues, https://github.com/yourusername/youngjin-langchain-tools/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/yourusername/youngjin-langchain-tools/releases
|
|
10
|
-
Author-email:
|
|
11
|
-
Maintainer-email:
|
|
10
|
+
Author-email: CoCoRoF <gkfua00@gmail.com>
|
|
11
|
+
Maintainer-email: CoCoRoF <gkfua00@gmail.com>
|
|
12
12
|
License: Apache-2.0
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Keywords: agents,ai,callback,handler,langchain,langgraph,llm,streamlit
|
|
@@ -16,12 +16,13 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
16
16
|
Classifier: Intended Audience :: Developers
|
|
17
17
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
22
23
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
24
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
-
Requires-Python: >=3.
|
|
25
|
+
Requires-Python: >=3.10
|
|
25
26
|
Requires-Dist: langchain-core<1.2.7,>=1.0.0
|
|
26
27
|
Requires-Dist: langchain<1.2.7,>=1.0.0
|
|
27
28
|
Requires-Dist: pydantic>=2.0.0
|
{youngjin_langchain_tools-0.1.1.dist-info → youngjin_langchain_tools-0.1.3.dist-info}/RECORD
RENAMED
|
@@ -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=
|
|
3
|
+
youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py,sha256=TDWjcgthh9eFPnufELbo4gzjUCCSYro6fUSRpwSXYuY,21540
|
|
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.
|
|
7
|
-
youngjin_langchain_tools-0.1.
|
|
8
|
-
youngjin_langchain_tools-0.1.
|
|
9
|
-
youngjin_langchain_tools-0.1.
|
|
6
|
+
youngjin_langchain_tools-0.1.3.dist-info/METADATA,sha256=0hGrXQcNkFakDVdfvVdk14CXuciF7f5GR_RXP0QjM10,6959
|
|
7
|
+
youngjin_langchain_tools-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
youngjin_langchain_tools-0.1.3.dist-info/licenses/LICENSE,sha256=fENUlkDDxJEn5c0u8mhVcz5ek-rTg2L4Kby1tMNchI8,11342
|
|
9
|
+
youngjin_langchain_tools-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|