Repository navigation
feat: add iter_observation_pages helper for observations v2 - #2101
Merged
Merged
Conversation
Since langfuse/langfuse#19043, GET /api/public/v2/observations can return short or empty pages while meta.cursor is set, and a request can take up to 45 s, well past the SDK's 5 s default timeout. Add iter_observation_pages and aiter_observation_pages, which page until the cursor is absent and send a 60 s per-request timeout, so callers don't have to hand-roll the loop.
This was referenced Nov 5, 2026
|
|
DavidTraina
marked this pull request as ready for review
November 5, 2026 07:03
A caller that mutates page.data could otherwise make max_items undercount.
Expose iter_observation_pages and aiter_observation_pages as Langfuse methods with typed parameters mirroring observations.get_many, instead of top-level functions that take the API client as an argument. The paging loop stays in langfuse/_client/pagination.py. Co-authored-by: Hassieb Pakzad <hassiebp@users.noreply.github.com>
Collaborator
|
@claude review |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds
iter_observation_pagesandaiter_observation_pages, which page throughGET /api/public/v2/observationscorrectly. Since langfuse/langfuse#19043, getting this right by hand takes more care. Pages can be short or empty whilemeta.cursoris set, and a request can take up to 45 s, well past the SDK's 5 s default timeout. Both loops in this repo stopped on empty pages (#2097, #2099).Behavior
meta.cursoris absent and yields every page, including empty ones. It yields pages, not items, sopage.meta.cursoris the exact resume point.timeout_in_seconds=60per request unless the caller'srequest_optionssets one. Other options pass through. The global timeout is left alone, because raising it also lengthens OTLP export,flush()andshutdown().max_items, the last request asks only for the remaining rows, so the last page's cursor resumes right after them.**paramsand forwards them toget_many, so it keeps working when the generated client gains parameters.Considered and not done
pagination:config. Fern's PythonSyncPagerstops on an empty page, andget_manywould return a pager instead ofObservationsV2Response, which is a breaking change.id, rewritingfrom_start_time/to_start_timeinto a JSONfilter, and automatic narrowing after a 422. Each adds request rewriting or depends on server column names or the cursor format. These are documented instead.run_batched_evaluationandtests/supportkeep their own loops for now, so this PR merges independently of the sibling PRs. They can move ontoiter_observation_pagesin a follow-up.Related PRs
Independent follow-ups to langfuse/langfuse#19043, mergeable in any order: #2097 (batch evaluation stops on empty pages), #2096 (batch evaluation fetch timeout), #2098 (docs), #2099 (test helpers).
Type of change
Verification
List the main commands you ran:
Making the loop stop on empty pages fails 6 of the 10 new tests. One test runs the real
LangfuseAPI/AsyncLangfuseAPIoverhttpx.MockTransportand checks that requests go out with a 60 s read timeout while the client is configured for 5 s.Checklist
code_review.md..env.templateif needed.The PR appears safe to merge, with a non-blocking fix recommended for counting rows before yielding pages.
Summary
Adds public sync and async helpers for paging through observations v2.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Start iterator] --> B[Choose request size] B --> C[Fetch page with per-request timeout] C --> D[Yield page, including empty pages] D --> E[Count rows and read cursor] E --> F{Cursor exists and item limit not reached?} F -->|Yes| B F -->|No| G[Stop]Reviews (1) · Last reviewed commit: "feat: add iter_observation_pages helper ..." · Reviewed by Greptile
What does this PR do?
Adds
Langfuse.iter_observation_pagesandLangfuse.aiter_observation_pages, which page throughGET /api/public/v2/observationscorrectly. Since langfuse/langfuse#19043, getting this right by hand takes more care. Pages can be short or empty whilemeta.cursoris set, and a request can take up to 45 s, well past the SDK's 5 s default timeout. Both loops in this repo stopped on empty pages (#2097, #2099).Behavior
meta.cursoris absent and yields every page, including empty ones. It yields pages, not items, sopage.meta.cursoris the exact resume point.timeout_in_seconds=60per request unless the caller'srequest_optionssets one. Other options pass through. The global timeout is left alone, because raising it also lengthens OTLP export,flush()andshutdown().max_items, the last request asks only for the remaining rows, so the last page's cursor resumes right after them.Langfuseclient next toget_datasetandget_prompt, usingapiandasync_api. The paging loop is private inlangfuse/_client/pagination.py.observations.get_many(except the deprecatedparse_io_as_json), so IDEs complete them and mypy checks them. A newget_manyparameter has to be added here too.Considered and not done
pagination:config. Fern's PythonSyncPagerstops on an empty page, andget_manywould return a pager instead ofObservationsV2Response, which is a breaking change.id, rewritingfrom_start_time/to_start_timeinto a JSONfilter, and automatic narrowing after a 422. Each adds request rewriting or depends on server column names or the cursor format. These are documented instead.run_batched_evaluationandtests/supportkeep their own loops for now, so this PR merges independently of the sibling PRs. They can move ontoiter_observation_pagesin a follow-up.Related PRs
Independent follow-ups to langfuse/langfuse#19043, mergeable in any order: #2097 (batch evaluation stops on empty pages), #2096 (batch evaluation fetch timeout), #2098 (docs), #2099 (test helpers).
Type of change
Verification
List the main commands you ran:
Making the loop stop on empty pages fails 6 of the 10 new tests. The tests call the
Langfusemethods. One runs the realLangfuseAPI/AsyncLangfuseAPIoverhttpx.MockTransportand checks that requests carrytraceIdand go out with a 60 s read timeout while the client is configured for 5 s.Checklist
code_review.md..env.templateif needed.