Python v3 → v4
Python v3 → v4
Python SDK v4는 observations 우선 데이터 모델 을 도입해요. 이 모델에서는 연관 속성(user_id, session_id, metadata, tags)이 트레이스에만 존재하는 것이 아니라 모든 observation으로 전파돼요. 이를 통해 비싼 join 없이 단일 테이블 쿼리가 가능해져 대규모에서 쿼리 성능이 크게 향상돼요.
출처: 문서
본문
이로 인해 트레이스 속성 설정 방식이 바뀌어요. update_current_trace()로 트레이스 객체를 명령형으로 업데이트하는 대신, propagate_attributes() — 현재 및 그 범위 안에서 생성된 모든 자식 observation에 속성을 자동으로 적용하는 컨텍스트 매니저를 사용해요.
v4는 기본 OpenTelemetry 내보내기 동작을 변경해요. Langfuse는 더 이상 기본적으로 모든 span을 내보내지 않아요. 이전에 비-LLM span(HTTP, DB, queue, 프레임워크 내부)이 전달되는 것에 의존했다면, 업그레이드 전에 아래 첫 번째 호환성 파괴 변경을 검토하세요.
호환성 파괴 변경
스마트 기본 span 필터링이 export-all 동작을 대체함
이전 버전에서는 기본적으로 모든 OpenTelemetry span을 내보내서 인프라 및 비-LLM 계측(HTTP, DB, queue, 프레임워크 내부)의 트레이스 노이즈가 증가했어요. 트레이스를 집중적이고 유용하게 유지하기 위해 v4는 스마트 기본 span 필터를 도입해요.
기본적으로 v4는 다음 중 하나라도 참이면 span을 내보내요:
- span이 Langfuse(langfuse-sdk)로 생성됨
- span에
gen_ai.*속성이 있음 - span 계측 범위가 알려진 LLM 범위 접두사와 일치함(예: openinference, langsmith, haystack, litellm)
v4 이전에는 차단되지 않은 계측 범위가 기본적으로 내보내졌어요.
pre-v4 "모두 내보내기" 동작 유지하기
from langfuse import Langfuse
langfuse = Langfuse(should_export_span=lambda span: True)
기본 동작과 커스텀 필터 조합하기
from langfuse import Langfuse
from langfuse.span_filter import is_default_export_span
langfuse = Langfuse(
should_export_span=lambda span: (
is_default_export_span(span)
or (
span.instrumentation_scope is not None
and span.instrumentation_scope.name.startswith("my_framework")
)
)
)
Python 호환성 참고: blocked_instrumentation_scopes는 deprecated
blocked_instrumentation_scopes는 v4에서 여전히 동작하지만 deprecated이며 향후 버전에서 제거될 거예요. should_export_span으로 마이그레이션하세요.
should_export_span로 동일한 차단목록 동작:
from langfuse import Langfuse
from langfuse.span_filter import is_default_export_span
blocked = {"sqlite", "requests"}
langfuse = Langfuse(
should_export_span=lambda span: (
is_default_export_span(span)
and (
span.instrumentation_scope is None
or span.instrumentation_scope.name not in blocked
)
)
)
blocked_instrumentation_scopes와 should_export_span이 모두 설정되면 차단된 범위가 여전히 우선(강한 거부권).
가능한 트레이스 트리 부작용과 디버깅 방법
중간 또는 부모 span이 드롭되는 동안 자식 span이 여전히 내보내지면 필터링이 트레이스 트리를 깨뜨릴 수 있어요. 트레이스가 끊겨 보이면 SDK 디버그 로깅을 켜 드롭된 span을 검사한 다음 콜백에서 필요한 범위를 허용 목록에 추가하세요.
- Python 디버그 모드:
Langfuse(debug=True)사용 또는LANGFUSE_DEBUG="True"설정. - SDK 고급 기능 및 원치 않는 span 관련 OpenTelemetry 트러블슈팅 참고.
update_current_trace() → 3개의 메서드로 분해
새 모델에서는 연관 속성(user_id, session_id, metadata, tags, 요청 범위 환경)이 트레이스뿐 아니라 모든 observation에 존재해야 해요. 그래서 propagate_attributes() — 해당 속성을 범위 안에서 생성된 현재 및 모든 자식 observation에 자동으로 적용하는 컨텍스트 매니저로 이동하는 거예요.
v3:
langfuse.update_current_trace(
name="trace-name",
user_id="user-123",
session_id="session-abc",
version="1.0",
input={"query": "hello"},
output={"result": "world"},
metadata={"key": "value"},
tags=["tag1"],
public=True,
)
v4 (분해됨):
from langfuse import observe, propagate_attributes, get_client
langfuse = get_client()
@observe()
def my_function():
# (a) Correlating attributes → propagate_attributes() context manager
with propagate_attributes(
trace_name="trace-name", # note: 'name' is now 'trace_name'
user_id="user-123",
session_id="session-abc",
version="1.0",
metadata={"key": "value"},
tags=["tag1"],
environment="staging",
):
result = call_llm("hello")
# (b) Trace I/O (deprecated, only for legacy trace-level LLM-as-a-judge configurations)
langfuse.set_current_trace_io(input={"query": "hello"}, output={"result": result})
# (c) Public flag
langfuse.set_current_trace_as_public()
주요 차이점:
| 속성 | v3 | v4 |
| name | update_current_trace(name=...) | propagate_attributes(trace_name=...) |
| user_id, session_id, tags, version | update_current_trace(...) | propagate_attributes(...) |
| metadata | update_current_trace(metadata=any) | propagate_attributes(metadata=dict[str,str]) |
| input, output | update_current_trace(...) | set_current_trace_io(...) (deprecated) |
| public | update_current_trace(public=True) | set_current_trace_as_public() |
| release | update_current_trace(release=...) | LANGFUSE_RELEASE — 제거됨, 환경 변수 사용 |
| environment | update_current_trace(environment=...) | LANGFUSE_TRACING_ENVIRONMENT / Langfuse(environment=...) / propagate_attributes(environment=...) — 프로세스 수준 환경은 전자, 요청 범위 환경은 후자 사용 |
set_current_trace_io()는 deprecated이며 트레이스 입력/출력에 의존하는 트레이스 수준 LLM-as-a-judge 평가자와의 하위 호환을 위해서만 존재해요. 새 코드에서는 루트 observation에 직접 input/output을 설정하세요.
span.update_trace() → 3개의 메서드로 분해
동일한 분해가 observation 수준의 update_trace() 메서드에도 적용돼요.
v3:
span.update_trace(
name="trace-name",
user_id="user-123",
session_id="session-abc",
input={"query": "hello"},
output={"result": "world"},
public=True,
)
v4:
from langfuse import get_client, propagate_attributes
langfuse = get_client()
with langfuse.start_as_current_observation(as_type="span", name="my-operation") as span:
with propagate_attributes(trace_name="trace-name", user_id="user-123", session_id="session-abc"):
result = call_llm("hello")
span.set_trace_io(input={"query": "hello"}, output={"result": result}) # deprecated
span.set_trace_as_public()
통합(LangChain, OpenAI)의 경우 전달된 트레이스 속성은 이제 자식에게만 전파되며 트레이스로 거슬러 올라가지 않아요.
Public API 네임스페이스 재매핑 (api.*)
v4에서는 고성능 Public API 리소스가 기본값이 돼요. v2 별칭은 제거됐어요.
| v3 / 전환 이름 | v4 이름 |
| langfuse.api.observations_v_2 | langfuse.api.observations |
| langfuse.api.score_v_2 | langfuse.api.scores |
| langfuse.api.metrics_v_2 | langfuse.api.metrics |
| langfuse.api.observations | langfuse.api.legacy.observations_v1 (legacy v1) |
| langfuse.api.score | langfuse.api.legacy.score_v1 (legacy v1) |
| langfuse.api.metrics | langfuse.api.legacy.metrics_v1 (legacy v1) |
Python SDK v4 클라이언트가 셀프 호스트 Langfuse v3 서버를 일시적으로 조회해야 한다면 해당 langfuse.api.legacy.<resource>_v1 네임스페이스를 사용하세요.
새 기본 langfuse.api.observations와 langfuse.api.metrics 메서드는 Observations v2와 Metrics v2 엔드포인트를 가리키며, 이는 Langfuse v4(Langfuse Cloud 또는 v4로 업그레이드된 셀프 호스트 서버)가 필요해요. 셀프 호스트 Langfuse v3에서는 대신 langfuse.api.legacy.observations_v1과 langfuse.api.legacy.metrics_v1을 사용하세요. 셀프 호스트 호환성 매트릭스 참고.
Public API 엔드포인트 폐기는 v4 호환성 파괴 변경과 별개예요. 일부 API 메서드는 Python v4에서 호출 가능하지만 Langfuse v4에서 deprecated인 서버 엔드포인트를 호출해요. 업그레이드 후 deprecated API 마이그레이션 가이드의 Python SDK 메서드 매핑을 사용해 langfuse.api.trace.list(), langfuse.api.sessions.list(), langfuse.api.scores.get_many() 같은 메서드를 감사하세요.
start_span() / start_generation() → start_observation()
observation이 새 모델의 주요 개념이에요. as_type 파라미터가 있는 통합 start_observation() API가 별도 메서드를 대체해요.
| v3 | v4 |
| langfuse.start_span(name="x") | langfuse.start_observation(name="x") |
| langfuse.start_as_current_span(name="x") | langfuse.start_as_current_observation(name="x") |
| langfuse.start_generation(name="x", model="gpt-4") | langfuse.start_observation(name="x", as_type="generation", model="gpt-4") |
| langfuse.start_as_current_generation(name="x", model="gpt-4") | langfuse.start_as_current_observation(name="x", as_type="generation", model="gpt-4") |
| span.start_span(name="x") | span.start_observation(name="x") |
| span.start_as_current_span(name="x") | span.start_as_current_observation(name="x") |
| span.start_generation(name="x") | span.start_observation(name="x", as_type="generation") |
| span.start_as_current_generation(name="x") | span.start_as_current_observation(name="x", as_type="generation") |
DatasetItemClient.run() 제거 → Experiment SDK 사용
Experiment SDK(dataset.run_experiment())가 실험 속성(run 메타데이터, 데이터셋 아이템 연결) 전파를 내부에서 처리해요.
v3:
for item in dataset.items:
with item.run(run_name="my-run", run_metadata={...}) as span:
result = my_llm(item.input)
span.update(output=result)
v4:
from langfuse import get_client
dataset = get_client().get_dataset("my-dataset")
def my_task(*, item, **kwargs):
return my_llm(item.input)
dataset.run_experiment(name="my-run", task=my_task)
DatasetItem 객체는 동일한 데이터 속성(id, input, expected_output, metadata 등)을 갖지만 run() 메서드는 제거됐어요.
LangChain CallbackHandler: update_trace 파라미터 제거
핸들러는 이제 내부적으로 propagate_attributes()를 사용해요. update_trace 파라미터는 더 이상 없으며, 전달하면 TypeError가 발생해요.
v3:
from langfuse.langchain import CallbackHandler
handler = CallbackHandler(update_trace=True, trace_context={...})
v4:
handler = CallbackHandler(trace_context={...})
propagate_attributes()로 LangChain 호출을 둘러싸는 observation으로 감싸면 여전히 트레이스 속성(user_id, session_id, tags 등)을 설정할 수 있어요. v2 → v3 마이그레이션 가이드의 LangChain 통합 예시 또는 커스텀 트레이스 속성 문서를 참고하세요.
제거된 타입
다음 타입이 langfuse.types에서 제거됐어요:
| 제거된 타입 | 설명 |
| TraceMetadata | name, user_id, session_id, version, release, metadata, tags, public이 있는 TypedDict |
| ObservationParams | observation 필드가 있는 TraceMetadata 확장 TypedDict |
| MapValue, ModelUsage, PromptClient | 더 이상 langfuse.types에서 재-export되지 않음, langfuse.model에서 import |
Pydantic v1 지원 제거
SDK는 이제 Pydantic v2를 요구해요. 애플리케이션이 여전히 Pydantic v1을 사용한다면 pydantic.v1 호환성 shim을 사용해야 해요.
검증 변경
- 전파된 metadata: 이제
dict[str, str]이며 값은 200자로 제한됨(이전에는 Any). 비문자열 값은 문자열로 강제 변환. 한도를 초과하는 값은 경고와 함께 버려짐. - user_id, session_id: 최대 길이 200자의 문자열로 검증. 한도를 초과하는 값은 경고와 함께 버려짐.
마이그레이션 체크리스트
- 비-LLM OpenTelemetry span에 의존했던 트레이스/대시보드 감사: v4 기본 필터에서 더 이상 나타나지 않을 수 있음
- 필요하면
should_export_span=lambda span: True로 pre-v4 "모든 span 내보내기" 동작 유지 - 여전히
blocked_instrumentation_scopes를 사용한다면 deprecation 제거 전에should_export_span조합으로 마이그레이션 update_current_trace검색 →propagate_attributes()+set_current_trace_io()(레거시 트레이스 수준 LLM-as-a-judge 구성에 의존할 때만) +set_current_trace_as_public()로 분할.update_trace(검색 → observation 객체에서 동일한 분할start_span/start_generation검색 →start_observation으로 교체item.run(검색 →dataset.run_experiment()로 교체CallbackHandler(update_trace=검색 → 파라미터 제거- metadata 값이
dict[str, str]이며 값 ≤200자임을 확인 - 아직 v1이면 Pydantic을 v2로 업그레이드
api.observations_v_2/api.score_v_2/api.metrics_v_2검색 →api.observations/api.scores/api.metrics로 교체api.observations/api.score/api.metrics의 legacy v1 사용 검색 →api.legacy.observations_v1/api.legacy.score_v1/api.legacy.metrics_v1로 이동- Langfuse v3을 셀프 호스트한다면
api.legacy.observations_v1과api.legacy.metrics_v1을 사용; 기본api.observations/api.metrics는 Langfuse v4가 필요함(셀프 호스트 호환성 매트릭스 참고) - 남은
*_v_2별칭 참조 제거(v4에서 제거됨)
더 알아보기 (Learn more)
- 출처 문서: Python v3 → v4