Download Latest Version 0.65.14 -- SELECT-only really means SELECT-only in SQLChatAgent source code.zip (59.3 MB)
Email in envelope

Get an email when there's a new version of Langroid

Home / 0.65.12
Name Modified Size InfoDownloads / Week
Parent folder
langroid-0.65.12-py3-none-any.whl 2026-07-25 456.7 kB
langroid-0.65.12.tar.gz 2026-07-25 406.1 kB
0.65.12 -- stop_on_first_result waits for a real result, no wasted httpx transports on cache hits, lxml security floor source code.tar.gz 2026-07-25 58.8 MB
0.65.12 -- stop_on_first_result waits for a real result, no wasted httpx transports on cache hits, lxml security floor source code.zip 2026-07-25 59.3 MB
README.md 2026-07-25 5.5 kB
Totals: 5 Items   118.9 MB 0

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.

:::bash
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 (github.com) 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-None and 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 narrowed ImportError handling.

Upgrade

:::bash
pip install -U langroid

Full changelog: 0.65.11...0.65.12

Source: README.md, updated 2026-07-25