Python v4 → v5
For most users, v5 is a small upgrade. If you trace with @observe, start_as_current_observation(), or an integration, and you create scores and fetch prompts, your code keeps working as is. v5 needs Python 3.11+ and Langfuse Cloud or self-hosted Langfuse v4.59.0 or later.
Most removals in v5 were already deprecated in v4. If your code runs on v4 without a DeprecationWarning from langfuse, you're most likely unaffected apart from the minimum versions, direct api.scores calls, and run_batched_evaluation(). Python hides DeprecationWarning by default outside __main__, so check with python -W default::DeprecationWarning or under pytest.
pip install --upgrade "langfuse>=5"Are you affected?
You only need to change something if one of these applies to you.
New in v5:
- You self-host Langfuse v3, or v4 older than v4.59.0, or run Python 3.10,
openai<1, orlangchain-core<1→ Minimum versions - You call
api.scores.createorapi.legacy.score_v1.*directly →api.scorestakes request objects - You call
api.scores_v3.get_many_v3→ Score reads move toapi.scores.get_many - You call
run_batched_evaluation()→ Batched evaluation reads observations only - You read metadata span attributes in
mask_otel_spansor a custom exporter, run experiments withsample_ratebelow 1, or a proxy rejects gzip-compressed requests → Under the hood
Previously deprecated in v4, now removed:
- You call deprecated
langfuse.api.*methods, for exampleapi.trace.get,api.sessions,api.legacy.*,api.ingestion, orapi.scores.get_many→ Deprecated Public API methods removed - You call
set_current_trace_io()orspan.set_trace_io()→ Trace input/output setters removed - You call
get_dataset_runs(),get_dataset_run(), ordelete_dataset_run()→ Dataset run helpers removed - You pass
blocked_instrumentation_scopes→blocked_instrumentation_scopesremoved
If none of these apply, upgrade and you're done.
Breaking changes
These are new in v5. Ordered by how many users they affect.
Minimum versions
Affects: self-hosted Langfuse v3 or older than v4.59.0, Python 3.10, and the OpenAI and LangChain integrations on older libraries.
| Requirement | v5 | If not met |
|---|---|---|
| Langfuse | Cloud, or self-hosted v4.59.0 or later | Unsupported. Upgrade Langfuse first, or stay on Python SDK v4 |
| Python | >=3.11 | pip install langfuse installs the latest v4 |
| OpenAI | openai>=1.0 | langfuse.openai no longer supports pre-1.0 SDKs |
| LangChain | langchain-core>=1 | ModuleNotFoundError when importing langfuse.langchain |
If you self-host Langfuse v3, stay on Python SDK v4. See the self-hosted compatibility matrix.
pip install --upgrade "langfuse>=5" "langchain-core>=1"The LangChain CallbackHandler now imports only from langchain_core, so you no longer need the langchain meta-package. Its import path and usage are unchanged.
api.scores takes request objects
Affects: direct calls to langfuse.api.scores.create(...) or langfuse.api.legacy.score_v1.*. create_score(), span.score(), and span.score_trace() are unchanged.
Breaks: TypeError for the old api.scores.create keyword arguments, and AttributeError for api.legacy.score_v1.
api.scores.create()takes a list ofCreateScoreRequestobjects asrequest=. It returnsCreateScoreBatchResponsewhen every score is accepted, orCreateScoreBatchResultswith accepted/rejected counts and errors.api.legacy.score_v1.create(...)is gone. Useapi.scores.create(request=[...]).api.legacy.score_v1.delete(score_id=...)moves toapi.scores.delete(score_id=...).
# v4
langfuse.api.scores.create(name="accuracy", value=0.9, trace_id="trace-id")
langfuse.api.legacy.score_v1.delete(score_id="score-id")
# v5
from langfuse.api import CreateScoreRequest
langfuse.api.scores.create(
request=[CreateScoreRequest(name="accuracy", value=0.9, trace_id="trace-id")]
)
langfuse.api.scores.delete(score_id="score-id")Score reads move to api.scores.get_many
Affects: calls to langfuse.api.scores_v3.get_many_v3(...) or langfuse.async_api.scores_v3.get_many_v3(...).
Breaks: AttributeError, because api.scores_v3 no longer exists. There is no alias.
Scores API v3 reads now live in the scores namespace. Arguments and the GetScoresV3Response response are unchanged. The v4 method of the same name, api.scores.get_many(score_ids=...), read Scores API v2. The v5 method reads Scores API v3 and filters by id= instead of score_ids=.
# v4
scores = langfuse.api.scores_v3.get_many_v3(trace_id="trace-id", fields="subject")
# v5
scores = langfuse.api.scores.get_many(trace_id="trace-id", fields="subject")Batched evaluation reads observations only
Affects: calls to langfuse.run_batched_evaluation().
Breaks (no prior deprecation): TypeError for scope, fetch_trace_fields, and _additional_trace_tags, and ValueError when resuming with a v4 token or with a different filter.
The function now reads from the v2 observations API and evaluates observations only.
scopeis removed. To evaluate whole traces, filter for the observations that represent them, for example bynameortype.fetch_trace_fieldsis renamed tofields(default"core,basic,io,metadata"). Field groups:core,basic,time,io,metadata,model,usage,prompt,metrics,trace_context.filtermust be a v2 filter JSON array.- The mapper receives
item=ObservationV2, whoseinputandoutputare raw strings. _additional_trace_tagsis removed.- v4 resume tokens can't be reused.
# v4
langfuse.run_batched_evaluation(
scope="traces",
mapper=trace_mapper,
filter='{"tags": ["production"]}',
fetch_trace_fields="core,io",
evaluators=[accuracy],
)
# v5
langfuse.run_batched_evaluation(
mapper=observation_mapper, # receives item=ObservationV2
filter='[{"type": "string", "column": "type", "operator": "=", "value": "GENERATION"}]',
fields="core,basic,io,model",
evaluators=[accuracy],
)Previously deprecated, now removed
v4 already marked each of these as deprecated and named the replacement. If you moved to the replacement, nothing changes.
Deprecated Public API methods removed
Affects: calls to the methods below through langfuse.api.*.
Breaks: AttributeError. Each method raised a DeprecationWarning in v4 that named its replacement. The api.legacy namespace is gone. For details, see the SDK method quick reference.
| Removed | Replacement |
|---|---|
api.trace.get(...), api.trace.list(...) | api.observations.get_many(trace_id=..., ...) |
api.sessions.* | api.observations.get_many(session_id=..., ...) |
api.legacy.observations_v1.* | api.observations.get_many(...) |
api.legacy.metrics_v1.metrics(...) | api.metrics.metrics(...) |
api.scores.get_many(...) (v2), api.scores.get_by_id(...) | api.scores.get_many(...) (Scores API v3) |
api.dataset_run_items.* | api.experiments.list_items(...) or the experiment runner |
api.datasets.get_run(...), .get_runs(...), .delete_run(...) | See Dataset run helpers removed |
api.ingestion.batch(...) | Tracing via OpenTelemetry (the SDK default), api.scores.create(request=[...]) for scores |
api.trace keeps delete and delete_multiple. api.datasets keeps list, get, and create. api.scores.get_many keeps its name but now reads Scores API v3; see Score reads move to api.scores.get_many.
# v4
trace = langfuse.api.trace.get("trace-id")
# v5
observations = langfuse.api.observations.get_many(trace_id="trace-id", fields="core,basic,io")The request and response types of the removed endpoints are removed too, for example Trace*, Session*, Observation/ObservationsView*, DatasetRun*, the ScoreV1 family, GetScoresResponse*, the v1 MetricsResponse, and the ingestion types (IngestionEvent, TraceBody, ObservationBody). langfuse.model no longer re-exports DatasetRun, Observation, or TraceWithFullDetails.
Trace input/output setters removed
Affects: calls to set_current_trace_io() or span.set_trace_io(), or code that sets the trace-level input/output attributes.
Breaks: AttributeError. In v4, both methods raised a DeprecationWarning. Trace input and output now come from the root observation, so set them there.
# v4
with langfuse.start_as_current_observation(as_type="span", name="request") as span:
langfuse.set_current_trace_io(input=query, output=result)
# v5
with langfuse.start_as_current_observation(as_type="span", name="request") as span:
span.update(input=query, output=result)
# or, inside the active root span: langfuse.update_current_span(input=..., output=...)Use propagate_attributes() for other trace attributes. set_current_trace_as_public() and span.set_trace_as_public() are unchanged. If you still use trace-level LLM-as-a-judge evaluators, migrate them to observation-level evaluators.
Dataset run helpers removed
Affects: calls to get_dataset_runs(), get_dataset_run(), or delete_dataset_run().
Breaks: AttributeError. The endpoints behind these helpers were deprecated in v4. Use the experiments API instead; "dataset run" and "experiment" mean the same thing.
| Removed | Replacement |
|---|---|
langfuse.get_dataset_runs(dataset_name=...) | langfuse.api.experiments.list(dataset_id=..., from_start_time=...) |
langfuse.get_dataset_run(dataset_name=..., run_name=...) | langfuse.api.experiments.list(...) and .list_items(...) |
langfuse.delete_dataset_run(dataset_name=..., run_name=...) | List the experiment items, then langfuse.api.trace.delete_multiple(...) |
See Dataset runs → Experiments for details.
blocked_instrumentation_scopes removed
Affects: code that passes blocked_instrumentation_scopes to Langfuse(...).
Breaks: TypeError. v4 raised a DeprecationWarning that pointed to should_export_span. Combine should_export_span with the default filter:
# v4
langfuse = Langfuse(blocked_instrumentation_scopes=["sqlite", "requests"])
# v5
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)
)New features
These need no code changes.
- Experiment links.
result.experiment_idandresult.experiment_urllink directly to the experiment comparison view. - Typed metadata filters. For data sent with v5, numeric and boolean metadata keeps its type, and nested objects can be filtered by dotted path, such as
a.b = 1. Data sent with v4 keeps its previous shape. - Batch score creation.
api.scores.create()accepts a list ofCreateScoreRequestobjects. - Experiment records. Run evaluations are stored for local-data runs too. Dataset versions are recorded on items as
langfuse.experiment.item.version. - Lighter LangChain dependency. The
CallbackHandlerneeds onlylangchain-core.
Under the hood
These need no code changes unless you depend on the old behavior.
- Scores are sent in batches to
POST /api/public/scoresinstead of the legacy ingestion endpoint.create_score(),span.score(),span.score_trace(),flush_at, andflush_intervalwork as before. If the server accepts only part of a batch (HTTP 207), the SDK logs the accepted and rejected counts and errors and doesn't retry the rejected scores. Other 4xx responses are logged and not retried. Network errors, 429, and 5xx are retried with backoff. - Metadata values are JSON-encoded on the span, strings included. The
langfuse.observation.metadata.<key>andlangfuse.trace.metadata.<key>attributes hold'"prod"'for the stringprodand'5'for the number5. Langfuse decodes them only for spans from Python SDK v5 or later, which is what keeps value types intact. Ifmask_otel_spansor a custom exporter reads or writes these attributes, decode withjson.loads()and write replacements withjson.dumps(). Propagated metadata values can be any JSON value; the 200-character limit applies to the encoded value, so a string can have at most 198 characters. Saved metadata filters written for the old, untyped shape may not match new data. - Experiments are recorded through OpenTelemetry.
run_experiment()sends experiment and item attributes on its spans instead of callingPOST /api/public/dataset-run-items. The runner doesn't special-case sampling: withsample_ratebelow 1, sampled-out items and their scores don't reach Langfuse, while run-level scores are always stored. Use a client with the defaultsample_rate=1.0for complete experiments. - Span batches are gzip-compressed. If a proxy rejects compressed requests, set
Langfuse(otel_compression="none")orLANGFUSE_OTEL_COMPRESSION=none. The standardOTEL_EXPORTER_OTLP_TRACES_COMPRESSIONandOTEL_EXPORTER_OTLP_COMPRESSIONvariables are honored too. A customspan_exporteris not changed. - The default span filter's GenAI check requires the
gen_ai.prefix, dot included, so attributes likegen_ai_customno longer count.
Migration checklist
Work through this list, or paste it into your coding agent. Skip items that don't apply.
- Use Python 3.11+ and Langfuse Cloud or self-hosted Langfuse v4.59.0+. On self-hosted v3, stay on v4.
-
pip install --upgrade "langfuse>=5". With integrations:openai>=1,langchain-core>=1. -
api.scores.create(name=..., value=..., ...)→api.scores.create(request=[CreateScoreRequest(...)]), and handleCreateScoreBatchResultsfor partial rejections. -
api.legacy.score_v1.create(...)→api.scores.create(request=[...]);api.legacy.score_v1.delete(score_id=...)→api.scores.delete(score_id=...). -
api.scores_v3.get_many_v3(...)→api.scores.get_many(...)(same forasync_api). - v2 score reads:
api.scores.get_many(score_ids=...)→api.scores.get_many(id=...);api.scores.get_by_id(...)→api.scores.get_many(id=...). -
run_batched_evaluation(): removescopeand_additional_trace_tags;fetch_trace_fields→fields; use a v2 filter JSON array; mapper receivesObservationV2; discard v4 resume tokens. -
api.trace.get/api.trace.list/api.sessions.*/api.legacy.observations_v1.*→api.observations.get_many(...). -
api.legacy.metrics_v1.metrics(...)→api.metrics.metrics(...). -
api.dataset_run_items.*,api.datasets.get_run(s),get_dataset_run(s)()→api.experiments.list(...)/api.experiments.list_items(...). -
delete_dataset_run()/api.datasets.delete_run()→ list experiment items, thenapi.trace.delete_multiple(trace_ids=[...]). -
api.ingestion.batch(...)→ SDK tracing (OpenTelemetry) for traces,api.scores.create(request=[...])for scores. -
set_current_trace_io()/span.set_trace_io()→span.update(input=..., output=...)on the root observation. -
blocked_instrumentation_scopes=[...]→should_export_spancombined withis_default_export_span. -
mask_otel_spansor custom exporters touchinglangfuse.*.metadata.<key>attributes:json.loads()values, writejson.dumps()values. - Experiments: run with
sample_rate=1.0. - Proxy rejects gzip: set
otel_compression="none"orLANGFUSE_OTEL_COMPRESSION=none.
Last updated on