This release includes 1 security fix for security teams reviewing exposed deployments.
Topics
+10 more
Affected surfaces
Summary
AI summaryUpdates Credit, before, and https://langroid.github.io/langroid/notes/batch-processing/ across a mixed release.
Full changelog
0.65.12 — stop_on_first_result waits for a real result, no wasted httpx transports on cache hits, lxml security floor
Three fixes this release: batch runs with stop_on_first_result=True no longer
give up after a fast None, the OpenAI client cache stops allocating an httpx
transport it immediately throws away, and the lxml floor moves to 6.1.0 to
clear two high-severity XXE alerts.
pip install -U langroid
stop_on_first_result now waits for the first valid result (#1072)
_process_batch_async(..., stop_on_first_result=True) called
asyncio.wait(..., FIRST_COMPLETED) exactly once, and its finally block then
cancelled every still-pending task — even when every completed task had mapped
to None. So a fast invalid result suppressed a slower valid one, contrary to
the documented behavior of continuing until a non-None result is obtained.
With the reproducer from #1068 (a fast task mapping to None, a slower valid
one, and a long-running one):
- before:
[None, None, None] - after:
[None, 'Processed valid', None]
The wait now repeats over the remaining task set until a non-None mapped
result appears or no tasks remain. Results keep their original input positions,
and only the tasks still pending at that point are cancelled. Exception
handling and cleanup stay on the same paths.
This matters most for the search-style idiom the flag was designed for: map any
unusable output to None from output_map and let the batch keep going. That
idiom silently didn't work whenever the first task to finish was a miss.
The stop_on_first_result semantics are now spelled out in the docstrings of
run_batch_task_gen, run_batch_tasks, and run_batch_agent_method —
including the interaction with handle_exceptions, where
ExceptionHandling.RETURN_NONE lets the batch keep waiting but
RETURN_EXCEPTION returns a non-None exception object that does stop it.
There is also a new note,
Batch Processing.
Behavior change: with stop_on_first_result=True, a batch in which every
task maps to None now runs to completion instead of returning early after the
first asyncio.wait round. It still returns an all-None list; it just no
longer cancels tasks that might yet produce a result.
No httpx transport construction on client-cache hits (#1048)
get_openai_client / get_async_openai_client built the httpx Client /
AsyncClient from http_client_config before the cache lookup, so every
cache hit allocated a transport that was immediately discarded — pure waste in
any workload that creates many agents sharing one client config. The transport
is now constructed only on a cache miss, inside the cache lock.
The same PR narrows an over-broad except ImportError: the try block wrapped
both from httpx import Client and the constructor call, so an ImportError
raised by httpx's own constructor (e.g. a socks:// proxy without socksio
installed) was misreported as "httpx is required to use http_client_config".
The handler now covers only the import; constructor errors propagate unchanged.
Fixes #1017.
lxml floor raised to 6.1.0 (#1071)
Clears two high-severity lxml advisories: iterparse() and
ETCompatXMLParser() shipped with entity resolution enabled by default before
6.1.0, so parsing untrusted XML could pull in external entities. Langroid parses
externally-fetched documents, so this was a reachable default rather than a
theoretical one.
The lxml<6.0.0 cap was langroid's alone — nothing else in the tree required
5.x — and 6.1.1 resolves with no other package movement. The constraint is now
lxml>=6.1.0,<7.0.0.
Docs: what full_eval=True actually disables
The code-injection-protection page claimed full_eval=True "removes all
protection". Since the fix for GHSA-q9p7-wqxg-mrhc, both eval sinks
(TableChatAgent.pandas_eval and VectorStore.compute_from_docs) pass
safe_eval_globals(vars), which restricts __builtins__ regardless of
full_eval — so the direct __import__('os') route is closed even in
full_eval mode.
The page now says what is true: full_eval=True disables the AST validator, the
restricted builtins remain, and they are not sufficient on their own, since an
expression can still walk the object graph of the exposed DataFrame. It also
points at SECURITY.md
so readers get the threat model rather than treating that page as a security
boundary.
Credit
- The batch fix was originally contributed by
@KXHXK in
#1069, reported as
#1068; this release ships
it with a deterministic regression test, all-Noneand exception-path
coverage, and docs. - The client-cache fix was originally contributed by
@shaun0927 in
#1018, and independently by
@Sanjays2402 in
#1067; this release ships it
with regression tests and the narrowedImportErrorhandling.
Upgrade
pip install -U langroid
Full changelog:
0.65.11...0.65.12
Security Fixes
- lxml floor raised to 6.1.0 — clears two high‑severity XXE advisories (iterparse and ETCompatXMLParser)
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
Related context
Related tools
Earlier breaking changes
- v0.65.9 MCP tool parameters now strictly validate enums, unions, and nested models.
- v0.65.5 Blocks code‑execution, file, and network primitives in creation tools by default.
- v0.65.5 Restricts retrieval tools to read‑only queries; write or admin clauses are rejected by default.
- v0.65.3 Raw user messages containing tools registered with `enable_message(..., use=False, handle=True)` are now dropped instead of executed.
- v0.65.2 Restricts eval'd expression builtins to a curated safe set, breaking code that relied on full Python builtins (e.g., __import__, open).
Beta — feedback welcome: [email protected]