-
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 returnAsyncPaginatorobjects that you can either:awaitto get the first page (what existing code does)async forto iterate through all items (what was broken)But our wrapper in
_patched_endpoint_asyncwas converting everything into coroutines, which broke theasync foruse 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_alistandtest_file_alistbecause 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
_TracedAsyncPaginatorwrapper 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
finallyblocks to ensure traces complete even if iteration stops early.Testing
Added two new pagination tests (
test_model_list_paginationandtest_model_alist_pagination) that specifically test theasync forpattern.