File size: 2,783 Bytes
515f392
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
LangSmith 좔적(tracing) μœ ν‹Έλ¦¬ν‹° λͺ¨λ“ˆ.

LangGraph λ…Έλ“œ 싀행을 LangSmithμ—μ„œ μΆ”μ ν•˜κ³  λͺ¨λ‹ˆν„°λ§ν•˜κΈ° μœ„ν•œ 도ꡬλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
곡식 λ¬Έμ„œ: https://docs.langchain.com/langsmith/trace-with-langgraph
"""

import os
import logging
import asyncio
from functools import wraps
from typing import Any, Callable
from inspect import iscoroutinefunction

from langsmith import traceable

logger = logging.getLogger(__name__)


def ensure_tracing_enabled() -> bool:
    """
    LangSmith 좔적이 μ˜¬λ°”λ₯΄κ²Œ μ„€μ •λ˜μ—ˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.
    
    Returns:
        bool: 좔적이 ν™œμ„±ν™”λ˜μ–΄ 있으면 True, κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ False
    """
    required_vars = ["LANGCHAIN_TRACING_V2", "LANGCHAIN_API_KEY"]
    
    missing_vars = [var for var in required_vars if not os.getenv(var)]
    
    if missing_vars:
        logger.warning(
            "LangSmith 좔적이 λΉ„ν™œμ„±ν™”λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λˆ„λ½λœ ν™˜κ²½λ³€μˆ˜: %s",
            ", ".join(missing_vars)
        )
        return False
    
    return True


def trace_node(node_name: str) -> Callable:
    """
    LangGraph λ…Έλ“œ 싀행을 μΆ”μ ν•˜λŠ” λ°μ½”λ ˆμ΄ν„°.
    
    이 λ°μ½”λ ˆμ΄ν„°λŠ” 각 λ…Έλ“œμ˜ μž…λ ₯/좜λ ₯, μ‹€ν–‰ μ‹œκ°„, μ—λŸ¬λ₯Ό 
    LangSmith λŒ€μ‹œλ³΄λ“œμ— μžλ™μœΌλ‘œ κΈ°λ‘ν•©λ‹ˆλ‹€.
    동기 및 비동기 ν•¨μˆ˜ λͺ¨λ‘ μ§€μ›ν•©λ‹ˆλ‹€.
    
    Args:
        node_name: LangSmith에 ν‘œμ‹œλ  λ…Έλ“œ 이름
    
    Returns:
        Callable: λ°μ½”λ ˆμ΄νŠΈλœ ν•¨μˆ˜
    
    Example:
        @trace_node("check_cache")
        async def check_cache_node(state: AgentState) -> AgentState:
            # λ…Έλ“œ 둜직
            return state
    """
    def decorator(func: Callable) -> Callable:
        # async ν•¨μˆ˜μΈμ§€ 확인
        if iscoroutinefunction(func):
            @wraps(func)
            @traceable(name=node_name, run_type="chain")
            async def async_wrapper(*args, **kwargs) -> Any:
                try:
                    result = await func(*args, **kwargs)
                    return result
                except Exception as e:
                    logger.error("πŸ”΄ λ…Έλ“œ μ‹€νŒ¨: %s - %s", node_name, str(e))
                    raise
            return async_wrapper
        else:
            @wraps(func)
            @traceable(name=node_name, run_type="chain")
            def sync_wrapper(*args, **kwargs) -> Any:
                try:
                    result = func(*args, **kwargs)
                    return result
                except Exception as e:
                    logger.error("πŸ”΄ λ…Έλ“œ μ‹€νŒ¨: %s - %s", node_name, str(e))
                    raise
            return sync_wrapper
    return decorator


# λͺ¨λ“ˆ import μ‹œ μžλ™μœΌλ‘œ 좔적 μ„€μ • 확인
ensure_tracing_enabled()