Skip to content

fix(sdk-python): recover prompt fetches and the cache lock in forked children - #159

Merged
aikins01 merged 1 commit into
mainfrom
fix/sdk-python/prompt-fork-safety
Oct 31, 2026
Merged

aikins01 merged 1 commit into
mainfrom
fix/sdk-python/prompt-fork-safety

Conversation

@aikins01

Copy link
Copy Markdown
Contributor

Two fork cases could leave a get_prompt call in a forked child waiting with no thread to wake it, and the prompt tests could count requests that other local processes send to the capture server.

A fork in a signal handler while get_prompt waits for a fetch copies that wait into the child, but not the thread that runs the fetch, so the child waited until the fetch timeout and then raised timeout (#107). The call now waits in steps of 0.1 s. When the process ID no longer matches the process of the fetch, the child starts its own fetch. A check only after the whole wait would still make the child wait for the timeout; the new test fails that way. A fork can still copy the event's internal lock while another thread holds it, which hangs the child for good; that window is a few microseconds and is #158.

A fork that runs no fork hooks, as a fork in C code, could copy the prompt cache lock while another thread held it, and the first get_prompt in the child then waited forever (#106). The cache now has one lock per process, keyed by os.getpid(), so a child with a new process ID never uses the parent's lock. The fork hooks still take the lock before a fork so that the child gets no half-changed entry. The child hook now drops the parent's locks, because a child can get the ID of an ended process whose lock is still in the map. The locks in _client, _export, _metrics, _spans and the provider packages that #106 also lists are not part of this change, so #106 stays open.

The prompt tests now give each capture server URL a random path and count only the requests under it (#114). Another process can still take a queued reply with a GET to the same port; that needs a change in tests/otlp_capture.py and is #156.

Closes #107
Closes #114
Refs #106

…children

A fork in a signal handler during a prompt fetch wait now starts a new fetch in the child instead of waiting for the timeout (#107). The prompt cache has one lock per process, keyed by os.getpid(), so a fork that runs no hooks cannot leave the child waiting for a lock held by a parent thread (#106). The prompt tests count only requests under a random path of the capture server URL (#114).
@aikins01 aikins01 self-assigned this Oct 31, 2026
@aikins01
aikins01 merged commit 1cfb936 into main Oct 31, 2026
10 checks passed
@aikins01 aikins01 mentioned this pull request Oct 31, 2026
@aikins01
aikins01 deleted the fix/sdk-python/prompt-fork-safety branch October 31, 2026 16:19
aikins01 added a commit that referenced this pull request Oct 31, 2026
Since the core SDKs record exception text only when both capture flags
are on (#88), the root, Bedrock, opencode, omp, and pi READMEs no longer
say that exception messages are recorded regardless of them. The root
README names the error messages that TanStack AI records.

Values are sent whole by default (#148), so tool definitions in Google
GenAI, Bedrock agent traces, and LiteLLM metadata are cut only at a
maxAttributeLength that is set, not at 65,536 characters. The Python
READMEs keep the old cap for telemetry-dev 0.3.2 and earlier, which
their dependency floors still allow.

The Python README says that a call in a child forked during a prompt
fetch looks at the cache again and starts a new fetch if it must (#159),
names the timeout of create_telemetry_span_exporter, and drops the note
about integrations that run on a core without record_output_chunk: every
integration requires telemetry-dev 0.3.0 or later, which has it.

The CLI README describes the trace summary and the suggested next
commands (#128). The experiment action README names the failed runs in
the comment, the pages it reads to find its comment (#157), and an empty
github-token. The root README lists prompts, experiments, and scores,
and the CI check of the packed npm exports (#155, #160). The opencode
plugin file example depends on ^0.2.2, not ^0.1.9.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant