youngjin-langchain-tools 0.1.0__tar.gz → 0.1.2__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.0 → youngjin_langchain_tools-0.1.2}/PKG-INFO +6 -5
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/pyproject.toml +8 -7
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py +165 -17
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/.gitignore +0 -0
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/LICENSE +0 -0
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/README.md +0 -0
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/youngjin_langchain_tools/__init__.py +0 -0
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/youngjin_langchain_tools/handlers/__init__.py +0 -0
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/youngjin_langchain_tools/utils/__init__.py +0 -0
- {youngjin_langchain_tools-0.1.0 → youngjin_langchain_tools-0.1.2}/youngjin_langchain_tools/utils/config.py +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youngjin-langchain-tools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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
|
|
@@ -4,16 +4,16 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "youngjin-langchain-tools"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "LangGraph utilities for Streamlit - StreamlitLanggraphHandler and more"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
license = {text = "Apache-2.0"}
|
|
12
12
|
authors = [
|
|
13
|
-
{name = "
|
|
13
|
+
{name = "CoCoRoF", email = "gkfua00@gmail.com"},
|
|
14
14
|
]
|
|
15
15
|
maintainers = [
|
|
16
|
-
{name = "
|
|
16
|
+
{name = "CoCoRoF", email = "gkfua00@gmail.com"},
|
|
17
17
|
]
|
|
18
18
|
keywords = [
|
|
19
19
|
"langchain",
|
|
@@ -30,9 +30,10 @@ classifiers = [
|
|
|
30
30
|
"Intended Audience :: Developers",
|
|
31
31
|
"License :: OSI Approved :: Apache Software License",
|
|
32
32
|
"Programming Language :: Python :: 3",
|
|
33
|
+
"Programming Language :: Python :: 3.10",
|
|
34
|
+
"Programming Language :: Python :: 3.11",
|
|
33
35
|
"Programming Language :: Python :: 3.12",
|
|
34
36
|
"Programming Language :: Python :: 3.13",
|
|
35
|
-
"Programming Language :: Python :: 3.14",
|
|
36
37
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
38
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
38
39
|
]
|
|
@@ -82,13 +83,13 @@ exclude = [
|
|
|
82
83
|
|
|
83
84
|
[tool.ruff]
|
|
84
85
|
line-length = 88
|
|
85
|
-
target-version = "
|
|
86
|
+
target-version = "py310"
|
|
86
87
|
|
|
87
88
|
[tool.ruff.lint]
|
|
88
89
|
select = ["E", "F", "W", "I", "N", "UP", "B", "C4"]
|
|
89
90
|
|
|
90
91
|
[tool.mypy]
|
|
91
|
-
python_version = "3.
|
|
92
|
+
python_version = "3.10"
|
|
92
93
|
warn_return_any = true
|
|
93
94
|
warn_unused_configs = true
|
|
94
95
|
disallow_untyped_defs = true
|
|
@@ -10,6 +10,116 @@ Replaces the deprecated StreamlitCallbackHandler for LangGraph-based agents.
|
|
|
10
10
|
|
|
11
11
|
from typing import Any, Dict, List, Optional, Union, Generator
|
|
12
12
|
from dataclasses import dataclass, field
|
|
13
|
+
import logging
|
|
14
|
+
import re
|
|
15
|
+
|
|
16
|
+
# Configure logging
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ============================================================
|
|
21
|
+
# Error Patterns for User-Friendly Messages
|
|
22
|
+
# ============================================================
|
|
23
|
+
ERROR_PATTERNS = {
|
|
24
|
+
# OpenAI errors
|
|
25
|
+
r"AuthenticationError.*API key|openai.*api.*key|OPENAI_API_KEY": {
|
|
26
|
+
"title": "🔑 OpenAI API Key 오류",
|
|
27
|
+
"message": "OpenAI API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
28
|
+
"solution": [
|
|
29
|
+
"환경변수 `OPENAI_API_KEY`를 설정하거나 클라이언트 초기화 시 `api_key` 파라미터를 전달하세요.",
|
|
30
|
+
"API 키 발급: https://platform.openai.com/api-keys",
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
r"RateLimitError|rate_limit|429": {
|
|
34
|
+
"title": "⏱️ Rate Limit 초과",
|
|
35
|
+
"message": "API 요청 한도를 초과했습니다.",
|
|
36
|
+
"solution": [
|
|
37
|
+
"잠시 후 다시 시도하거나, API 사용량 및 요금제를 확인하세요.",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
r"InsufficientQuotaError|insufficient_quota|billing": {
|
|
41
|
+
"title": "💳 크레딧 부족",
|
|
42
|
+
"message": "API 크레딧이 부족합니다.",
|
|
43
|
+
"solution": [
|
|
44
|
+
"API 제공자의 결제 페이지에서 크레딧을 충전하세요.",
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
r"InvalidRequestError|invalid_request": {
|
|
48
|
+
"title": "❌ 잘못된 요청",
|
|
49
|
+
"message": "API 요청 형식이 올바르지 않습니다.",
|
|
50
|
+
"solution": [
|
|
51
|
+
"입력 데이터와 모델명이 올바른지 확인하세요.",
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
# Anthropic errors
|
|
55
|
+
r"anthropic.*authentication|ANTHROPIC_API_KEY": {
|
|
56
|
+
"title": "🔑 Anthropic API Key 오류",
|
|
57
|
+
"message": "Anthropic API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
58
|
+
"solution": [
|
|
59
|
+
"환경변수 `ANTHROPIC_API_KEY`를 설정하세요.",
|
|
60
|
+
"API 키 발급: https://console.anthropic.com/",
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
# Google errors
|
|
64
|
+
r"google.*api.*key|GOOGLE_API_KEY": {
|
|
65
|
+
"title": "🔑 Google API Key 오류",
|
|
66
|
+
"message": "Google API 키가 설정되지 않았거나 유효하지 않습니다.",
|
|
67
|
+
"solution": [
|
|
68
|
+
"환경변수 `GOOGLE_API_KEY`를 설정하세요.",
|
|
69
|
+
"API 키 발급: https://aistudio.google.com/apikey",
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
# Network errors
|
|
73
|
+
r"ConnectionError|connection.*refused|network": {
|
|
74
|
+
"title": "🌐 네트워크 오류",
|
|
75
|
+
"message": "API 서버에 연결할 수 없습니다.",
|
|
76
|
+
"solution": [
|
|
77
|
+
"인터넷 연결 및 방화벽/프록시 설정을 확인하세요.",
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
r"TimeoutError|timeout|timed out": {
|
|
81
|
+
"title": "⏰ 시간 초과",
|
|
82
|
+
"message": "API 요청이 시간 초과되었습니다.",
|
|
83
|
+
"solution": [
|
|
84
|
+
"네트워크 연결을 확인하고 잠시 후 다시 시도하세요.",
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
# Model errors
|
|
88
|
+
r"model.*not.*found|does not exist|invalid.*model": {
|
|
89
|
+
"title": "🤖 모델 오류",
|
|
90
|
+
"message": "지정된 모델을 찾을 수 없습니다.",
|
|
91
|
+
"solution": [
|
|
92
|
+
"모델명과 접근 권한을 확인하세요.",
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _parse_error(error: Exception) -> Dict[str, Any]:
|
|
99
|
+
"""Parse an exception and return user-friendly error information."""
|
|
100
|
+
error_str = str(error)
|
|
101
|
+
error_type = type(error).__name__
|
|
102
|
+
full_error = f"{error_type}: {error_str}"
|
|
103
|
+
|
|
104
|
+
# Try to match known error patterns
|
|
105
|
+
for pattern, info in ERROR_PATTERNS.items():
|
|
106
|
+
if re.search(pattern, full_error, re.IGNORECASE):
|
|
107
|
+
return {
|
|
108
|
+
"matched": True,
|
|
109
|
+
"title": info["title"],
|
|
110
|
+
"message": info["message"],
|
|
111
|
+
"solution": info["solution"],
|
|
112
|
+
"original_error": error_str[:500], # Truncate for display
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Unknown error - return generic info
|
|
116
|
+
return {
|
|
117
|
+
"matched": False,
|
|
118
|
+
"title": "❗ 오류 발생",
|
|
119
|
+
"message": f"{error_type}",
|
|
120
|
+
"solution": ["에러 메시지를 확인하고 문제를 해결해주세요."],
|
|
121
|
+
"original_error": error_str[:500],
|
|
122
|
+
}
|
|
13
123
|
|
|
14
124
|
|
|
15
125
|
@dataclass
|
|
@@ -231,25 +341,63 @@ class StreamlitLanggraphHandler:
|
|
|
231
341
|
)
|
|
232
342
|
self._response_placeholder = st.empty()
|
|
233
343
|
|
|
234
|
-
# Stream from agent
|
|
344
|
+
# Stream from agent with error handling
|
|
235
345
|
config = config or {}
|
|
236
346
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
347
|
+
try:
|
|
348
|
+
for stream_mode, data in agent.stream(
|
|
349
|
+
input,
|
|
350
|
+
config=config,
|
|
351
|
+
stream_mode=["messages", "updates"]
|
|
352
|
+
):
|
|
353
|
+
if stream_mode == "updates":
|
|
354
|
+
yield from self._handle_updates(data)
|
|
355
|
+
elif stream_mode == "messages":
|
|
356
|
+
yield from self._handle_messages(data)
|
|
357
|
+
|
|
358
|
+
# Mark as complete
|
|
359
|
+
self._status_container.update(
|
|
360
|
+
label=self._config.complete_label,
|
|
361
|
+
state="complete",
|
|
362
|
+
expanded=False
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
# Parse error and display user-friendly message
|
|
367
|
+
error_info = _parse_error(e)
|
|
368
|
+
|
|
369
|
+
# Update status to show error
|
|
370
|
+
self._status_container.update(
|
|
371
|
+
label="❌ 오류 발생",
|
|
372
|
+
state="error",
|
|
373
|
+
expanded=True
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# Display error in status container
|
|
377
|
+
with self._status_container:
|
|
378
|
+
st.error(f"**{error_info['title']}**")
|
|
379
|
+
st.markdown(f"_{error_info['message']}_")
|
|
380
|
+
|
|
381
|
+
st.markdown("**해결 방법:**")
|
|
382
|
+
for solution in error_info["solution"]:
|
|
383
|
+
st.markdown(f" {solution}")
|
|
384
|
+
|
|
385
|
+
with st.expander("🔍 상세 에러 메시지", expanded=False):
|
|
386
|
+
st.code(error_info["original_error"], language="text")
|
|
387
|
+
|
|
388
|
+
# Log the full error for debugging
|
|
389
|
+
logger.error(f"Agent execution error: {e}", exc_info=True)
|
|
390
|
+
|
|
391
|
+
# Yield error event
|
|
392
|
+
yield {
|
|
393
|
+
"type": "error",
|
|
394
|
+
"data": {
|
|
395
|
+
"error_type": type(e).__name__,
|
|
396
|
+
"error_info": error_info,
|
|
397
|
+
"original_error": str(e),
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return # Stop further processing
|
|
253
401
|
|
|
254
402
|
# Final render without cursor
|
|
255
403
|
if self._final_response:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|