-
Notifications
You must be signed in to change notification settings - Fork 463
fix(openai): async pagination for OpenAI list methods #14911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Bootstrap import analysisComparison of import times between this PR and base. SummaryThe average import time from this PR is: 239 ± 1 ms. The average import time from base is: 241 ± 2 ms. The import time difference between this PR and base is: -2.32 ± 0.07 ms. Import time breakdownThe following import paths have shrunk:
|
Performance SLOsComparing candidate alex/MLOB-4193_fix-openai-model-listing-function (ae07e83) with baseline main (92937df) 📈 Performance Regressions (2 suites)📈 iastaspectsospath - 24/24✅ ospathbasename_aspectTime: ✅ 5.124µs (SLO: <10.000µs 📉 -48.8%) vs baseline: 📈 +18.0% Memory: ✅ 37.709MB (SLO: <39.000MB -3.3%) vs baseline: +4.9% ✅ ospathbasename_noaspectTime: ✅ 1.099µs (SLO: <10.000µs 📉 -89.0%) vs baseline: +0.4% Memory: ✅ 37.670MB (SLO: <39.000MB -3.4%) vs baseline: +4.8% ✅ ospathjoin_aspectTime: ✅ 6.105µs (SLO: <10.000µs 📉 -38.9%) vs baseline: -0.6% Memory: ✅ 37.690MB (SLO: <39.000MB -3.4%) vs baseline: +5.0% ✅ ospathjoin_noaspectTime: ✅ 2.316µs (SLO: <10.000µs 📉 -76.8%) vs baseline: +0.4% Memory: ✅ 37.670MB (SLO: <39.000MB -3.4%) vs baseline: +4.9% ✅ ospathnormcase_aspectTime: ✅ 3.494µs (SLO: <10.000µs 📉 -65.1%) vs baseline: ~same Memory: ✅ 37.729MB (SLO: <39.000MB -3.3%) vs baseline: +5.2% ✅ ospathnormcase_noaspectTime: ✅ 0.577µs (SLO: <10.000µs 📉 -94.2%) vs baseline: +0.7% Memory: ✅ 37.690MB (SLO: <39.000MB -3.4%) vs baseline: +5.0% ✅ ospathsplit_aspectTime: ✅ 4.901µs (SLO: <10.000µs 📉 -51.0%) vs baseline: +0.8% Memory: ✅ 37.690MB (SLO: <39.000MB -3.4%) vs baseline: +4.8% ✅ ospathsplit_noaspectTime: ✅ 1.607µs (SLO: <10.000µs 📉 -83.9%) vs baseline: -0.2% Memory: ✅ 37.690MB (SLO: <39.000MB -3.4%) vs baseline: +4.9% ✅ ospathsplitdrive_aspectTime: ✅ 3.685µs (SLO: <10.000µs 📉 -63.1%) vs baseline: +1.0% Memory: ✅ 37.690MB (SLO: <39.000MB -3.4%) vs baseline: +4.7% ✅ ospathsplitdrive_noaspectTime: ✅ 0.703µs (SLO: <10.000µs 📉 -93.0%) vs baseline: -0.2% Memory: ✅ 37.650MB (SLO: <39.000MB -3.5%) vs baseline: +4.9% ✅ ospathsplitext_aspectTime: ✅ 4.610µs (SLO: <10.000µs 📉 -53.9%) vs baseline: +0.6% Memory: ✅ 37.690MB (SLO: <39.000MB -3.4%) vs baseline: +4.9% ✅ ospathsplitext_noaspectTime: ✅ 1.391µs (SLO: <10.000µs 📉 -86.1%) vs baseline: -0.7% Memory: ✅ 37.670MB (SLO: <39.000MB -3.4%) vs baseline: +4.9% 📈 telemetryaddmetric - 30/30✅ 1-count-metric-1-timesTime: ✅ 3.312µs (SLO: <20.000µs 📉 -83.4%) vs baseline: +5.1% Memory: ✅ 32.086MB (SLO: <34.000MB -5.6%) vs baseline: +4.8% ✅ 1-count-metrics-100-timesTime: ✅ 211.662µs (SLO: <250.000µs 📉 -15.3%) vs baseline: -1.4% Memory: ✅ 32.126MB (SLO: <34.000MB -5.5%) vs baseline: +4.9% ✅ 1-distribution-metric-1-timesTime: ✅ 3.192µs (SLO: <20.000µs 📉 -84.0%) vs baseline: +7.7% Memory: ✅ 32.145MB (SLO: <34.000MB -5.5%) vs baseline: +4.9% ✅ 1-distribution-metrics-100-timesTime: ✅ 190.816µs (SLO: <220.000µs 📉 -13.3%) vs baseline: -0.5% Memory: ✅ 32.145MB (SLO: <34.000MB -5.5%) vs baseline: +5.1% ✅ 1-gauge-metric-1-timesTime: ✅ 2.059µs (SLO: <20.000µs 📉 -89.7%) vs baseline: -0.6% Memory: ✅ 32.185MB (SLO: <34.000MB -5.3%) vs baseline: +4.9% ✅ 1-gauge-metrics-100-timesTime: ✅ 124.435µs (SLO: <150.000µs 📉 -17.0%) vs baseline: +0.6% Memory: ✅ 32.126MB (SLO: <34.000MB -5.5%) vs baseline: +4.9% ✅ 1-rate-metric-1-timesTime: ✅ 3.383µs (SLO: <20.000µs 📉 -83.1%) vs baseline: +7.0% Memory: ✅ 32.165MB (SLO: <34.000MB -5.4%) vs baseline: +4.9% ✅ 1-rate-metrics-100-timesTime: ✅ 212.554µs (SLO: <250.000µs 📉 -15.0%) vs baseline: -0.3% Memory: ✅ 32.106MB (SLO: <34.000MB -5.6%) vs baseline: +4.9% ✅ 100-count-metrics-100-timesTime: ✅ 21.740ms (SLO: <23.500ms -7.5%) vs baseline: +1.3% Memory: ✅ 32.185MB (SLO: <34.000MB -5.3%) vs baseline: +5.2% ✅ 100-distribution-metrics-100-timesTime: ✅ 1.970ms (SLO: <2.250ms 📉 -12.4%) vs baseline: -0.6% Memory: ✅ 32.086MB (SLO: <34.000MB -5.6%) vs baseline: +4.7% ✅ 100-gauge-metrics-100-timesTime: ✅ 1.282ms (SLO: <1.550ms 📉 -17.3%) vs baseline: ~same Memory: ✅ 32.086MB (SLO: <34.000MB -5.6%) vs baseline: +4.6% ✅ 100-rate-metrics-100-timesTime: ✅ 2.235ms (SLO: <2.550ms 📉 -12.4%) vs baseline: +1.2% Memory: ✅ 32.126MB (SLO: <34.000MB -5.5%) vs baseline: +4.9% ✅ flush-1-metricTime: ✅ 4.751µs (SLO: <20.000µs 📉 -76.2%) vs baseline: 📈 +12.4% Memory: ✅ 32.106MB (SLO: <34.000MB -5.6%) vs baseline: +4.9% ✅ flush-100-metricsTime: ✅ 180.552µs (SLO: <250.000µs 📉 -27.8%) vs baseline: -1.2% Memory: ✅ 32.165MB (SLO: <34.000MB -5.4%) vs baseline: +5.2% ✅ flush-1000-metricsTime: ✅ 2.229ms (SLO: <2.500ms 📉 -10.8%) vs baseline: +0.3% Memory: ✅ 32.873MB (SLO: <34.500MB -4.7%) vs baseline: +4.9% 🟡 Near SLO Breach (4 suites)🟡 djangosimple - 30/30✅ appsecTime: ✅ 20.475ms (SLO: <22.300ms -8.2%) vs baseline: +0.1% Memory: ✅ 65.458MB (SLO: <67.000MB -2.3%) vs baseline: +4.7% ✅ exception-replay-enabledTime: ✅ 1.348ms (SLO: <1.450ms -7.0%) vs baseline: +0.1% Memory: ✅ 64.546MB (SLO: <67.000MB -3.7%) vs baseline: +4.8% ✅ iastTime: ✅ 20.408ms (SLO: <22.250ms -8.3%) vs baseline: -0.2% Memory: ✅ 65.475MB (SLO: <67.000MB -2.3%) vs baseline: +4.9% ✅ profilerTime: ✅ 15.235ms (SLO: <16.550ms -7.9%) vs baseline: ~same Memory: ✅ 53.669MB (SLO: <54.500MB 🟡 -1.5%) vs baseline: +4.9% ✅ resource-renamingTime: ✅ 20.508ms (SLO: <21.750ms -5.7%) vs baseline: -0.2% Memory: ✅ 65.399MB (SLO: <67.000MB -2.4%) vs baseline: +4.7% ✅ span-code-originTime: ✅ 26.147ms (SLO: <28.200ms -7.3%) vs baseline: -0.3% Memory: ✅ 67.605MB (SLO: <69.500MB -2.7%) vs baseline: +4.9% ✅ tracerTime: ✅ 20.458ms (SLO: <21.750ms -5.9%) vs baseline: +0.2% Memory: ✅ 65.481MB (SLO: <67.000MB -2.3%) vs baseline: +4.8% ✅ tracer-and-profilerTime: ✅ 22.078ms (SLO: <23.500ms -6.1%) vs baseline: +0.3% Memory: ✅ 66.688MB (SLO: <67.500MB 🟡 -1.2%) vs baseline: +5.0% ✅ tracer-dont-create-db-spansTime: ✅ 19.286ms (SLO: <21.500ms 📉 -10.3%) vs baseline: -0.1% Memory: ✅ 65.440MB (SLO: <66.000MB 🟡 -0.8%) vs baseline: +4.8% ✅ tracer-minimalTime: ✅ 16.644ms (SLO: <17.500ms -4.9%) vs baseline: ~same Memory: ✅ 65.375MB (SLO: <66.000MB 🟡 -0.9%) vs baseline: +4.7% ✅ tracer-nativeTime: ✅ 20.448ms (SLO: <21.750ms -6.0%) vs baseline: -0.4% Memory: ✅ 71.381MB (SLO: <72.500MB 🟡 -1.5%) vs baseline: +4.8% ✅ tracer-no-cachesTime: ✅ 18.357ms (SLO: <19.650ms -6.6%) vs baseline: -0.6% Memory: ✅ 65.493MB (SLO: <67.000MB -2.2%) vs baseline: +4.9% ✅ tracer-no-databasesTime: ✅ 18.757ms (SLO: <20.100ms -6.7%) vs baseline: -0.2% Memory: ✅ 65.349MB (SLO: <67.000MB -2.5%) vs baseline: +4.8% ✅ tracer-no-middlewareTime: ✅ 20.139ms (SLO: <21.500ms -6.3%) vs baseline: -0.3% Memory: ✅ 65.472MB (SLO: <67.000MB -2.3%) vs baseline: +5.0% ✅ tracer-no-templatesTime: ✅ 20.254ms (SLO: <22.000ms -7.9%) vs baseline: -0.2% Memory: ✅ 65.539MB (SLO: <67.000MB -2.2%) vs baseline: +4.9% 🟡 errortrackingdjangosimple - 6/6✅ errortracking-enabled-allTime: ✅ 18.276ms (SLO: <19.850ms -7.9%) vs baseline: +1.0% Memory: ✅ 65.235MB (SLO: <66.500MB 🟡 -1.9%) vs baseline: +4.9% ✅ errortracking-enabled-userTime: ✅ 18.167ms (SLO: <19.400ms -6.4%) vs baseline: +0.4% Memory: ✅ 65.274MB (SLO: <66.500MB 🟡 -1.8%) vs baseline: +5.0% ✅ tracer-enabledTime: ✅ 18.150ms (SLO: <19.450ms -6.7%) vs baseline: +0.3% Memory: ✅ 65.294MB (SLO: <66.500MB 🟡 -1.8%) vs baseline: +5.0% 🟡 flasksimple - 18/18✅ appsec-getTime: ✅ 4.580ms (SLO: <4.750ms -3.6%) vs baseline: +0.1% Memory: ✅ 62.030MB (SLO: <65.000MB -4.6%) vs baseline: +5.0% ✅ appsec-postTime: ✅ 6.588ms (SLO: <6.750ms -2.4%) vs baseline: -0.1% Memory: ✅ 61.971MB (SLO: <65.000MB -4.7%) vs baseline: +4.8% ✅ appsec-telemetryTime: ✅ 4.570ms (SLO: <4.750ms -3.8%) vs baseline: ~same Memory: ✅ 61.971MB (SLO: <65.000MB -4.7%) vs baseline: +4.8% ✅ debuggerTime: ✅ 1.855ms (SLO: <2.000ms -7.3%) vs baseline: ~same Memory: ✅ 45.475MB (SLO: <47.000MB -3.2%) vs baseline: +5.0% ✅ iast-getTime: ✅ 1.861ms (SLO: <2.000ms -6.9%) vs baseline: ~same Memory: ✅ 42.389MB (SLO: <49.000MB 📉 -13.5%) vs baseline: +5.1% ✅ profilerTime: ✅ 1.910ms (SLO: <2.100ms -9.1%) vs baseline: ~same Memory: ✅ 46.537MB (SLO: <47.000MB 🟡 -1.0%) vs baseline: +4.9% ✅ resource-renamingTime: ✅ 3.370ms (SLO: <3.650ms -7.7%) vs baseline: ~same Memory: ✅ 52.258MB (SLO: <53.500MB -2.3%) vs baseline: +4.8% ✅ tracerTime: ✅ 3.359ms (SLO: <3.650ms -8.0%) vs baseline: +0.3% Memory: ✅ 52.239MB (SLO: <53.500MB -2.4%) vs baseline: +4.9% ✅ tracer-nativeTime: ✅ 3.361ms (SLO: <3.650ms -7.9%) vs baseline: ~same Memory: ✅ 58.216MB (SLO: <60.000MB -3.0%) vs baseline: +5.1% 🟡 otelspan - 22/22✅ add-eventTime: ✅ 41.036ms (SLO: <47.150ms 📉 -13.0%) vs baseline: -0.2% Memory: ✅ 44.183MB (SLO: <47.000MB -6.0%) vs baseline: +5.0% ✅ add-metricsTime: ✅ 315.519ms (SLO: <344.800ms -8.5%) vs baseline: -1.3% Memory: ✅ 616.702MB (SLO: <630.000MB -2.1%) vs baseline: +5.0% ✅ add-tagsTime: ✅ 287.763ms (SLO: <314.000ms -8.4%) vs baseline: ~same Memory: ✅ 618.643MB (SLO: <630.000MB 🟡 -1.8%) vs baseline: +5.0% ✅ get-contextTime: ✅ 81.052ms (SLO: <92.350ms 📉 -12.2%) vs baseline: +0.3% Memory: ✅ 39.783MB (SLO: <46.500MB 📉 -14.4%) vs baseline: +5.0% ✅ is-recordingTime: ✅ 38.238ms (SLO: <44.500ms 📉 -14.1%) vs baseline: ~same Memory: ✅ 43.588MB (SLO: <47.500MB -8.2%) vs baseline: +4.8% ✅ record-exceptionTime: ✅ 59.366ms (SLO: <67.650ms 📉 -12.2%) vs baseline: +2.0% Memory: ✅ 40.050MB (SLO: <47.000MB 📉 -14.8%) vs baseline: +4.6% ✅ set-statusTime: ✅ 44.056ms (SLO: <50.400ms 📉 -12.6%) vs baseline: -0.4% Memory: ✅ 43.598MB (SLO: <47.000MB -7.2%) vs baseline: +4.9% ✅ startTime: ✅ 37.552ms (SLO: <43.450ms 📉 -13.6%) vs baseline: ~same Memory: ✅ 43.527MB (SLO: <47.000MB -7.4%) vs baseline: +4.7% ✅ start-finishTime: ✅ 82.662ms (SLO: <88.000ms -6.1%) vs baseline: +0.2% Memory: ✅ 34.544MB (SLO: <46.500MB 📉 -25.7%) vs baseline: +4.9% ✅ start-finish-telemetryTime: ✅ 84.169ms (SLO: <89.000ms -5.4%) vs baseline: +0.4% Memory: ✅ 34.505MB (SLO: <46.500MB 📉 -25.8%) vs baseline: +4.7% ✅ update-nameTime: ✅ 39.251ms (SLO: <45.150ms 📉 -13.1%) vs baseline: ~same Memory: ✅ 43.966MB (SLO: <47.000MB -6.5%) vs baseline: +4.8%
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
release note lgtm
Hey, so this is an attempt to fix #14574 where doing
async for model in client.models.list()
would fail withTypeError: 'async for' requires an object with __aiter__ method, got coroutine
.The Problem
Methods like
AsyncModels.list()
andAsyncFiles.list()
don't actually return coroutines - they returnAsyncPaginator
objects that you can either:await
to get the first page (what existing code does)async for
to iterate through all items (what was broken)But our wrapper in
_patched_endpoint_async
was converting everything into coroutines, which broke theasync for
use case.What I Tried
First attempt was using
inspect.iscoroutinefunction()
to detect which methods are actually async vs just returning async objects. That got messy fast because checking unbound methods from classes didn't work reliably.Then I tried just using the sync wrapper for list methods:
This looked promising - the pagination tests passed! But it broke
test_model_alist
andtest_file_alist
because those tests doawait client.models.list()
and expect full tracing with response metadata likeopenai.response.count
. Using the sync wrapper meant we lost all that when the paginator was awaited.Also tried returning the paginator directly without any wrapping, but that meant we lost tracing entirely when someone did
async for
. Not acceptable.A Solution
Created a
_TracedAsyncPaginator
wrapper class that implements both__aiter__
and__await__
. This way:await client.models.list()
-> calls__await__
, traces properly, returns first page (existing behavior preserved)async for model in client.models.list()
-> calls__aiter__
, traces on first iteration, yields items (fixes the bug)The wrapper is ~50 lines but it's the minimal solution that preserves 100% backward compatibility while fixing the breaking bug. Had to use
finally
blocks to ensure traces complete even if iteration stops early.Testing
Added two new pagination tests (
test_model_list_pagination
andtest_model_alist_pagination
) that specifically test theasync for
pattern.