Skip to content

feat(py): close querychat-created chatlas clients in cleanup() - #286

Draft
cpsievert wants to merge 3 commits into
mainfrom
cleanup-lifecycle-py
Draft

feat(py): close querychat-created chatlas clients in cleanup()#286
cpsievert wants to merge 3 commits into
mainfrom
cleanup-lifecycle-py

Conversation

@cpsievert

Copy link
Copy Markdown
Contributor

Motivation

chatlas PR #427 adds Chat.close()/close_async() to release provider resources (HTTP connection pools, Snowflake sessions, MCP sessions). This PR adopts that API in querychat under a clear ownership rule:

querychat closes a chatlas client if and only if querychat created it — i.e., client was None (deferred env-var/default resolution) or a string spec like "openai/gpt-4o" resolved via ChatAuto. User-supplied Chat instances are never closed by querychat.

Changes

  • QueryChatBase.__init__ records an _owns_client flag at construction (the original client argument is discarded after resolution, so ownership must be captured there)
  • QueryChatBase.cleanup() now closes the base client when owned, in addition to the existing query-executor and data-source cleanup
  • Temporarily pins chatlas to the PR branch in pyproject.tomlmust be replaced with a released chatlas>=X.Y.Z before this merges (TODO comment in place)
  • CHANGELOG entry

Design notes

  • No per-session session.on_ended handler. Investigation showed every closable resource (data sources, query executor, DuckDB connection, base chat client) is app-scoped on QueryChatBase and shared across sessions; session-scoped state is pure reactive values reclaimed by GC. Cleanup is an app-shutdown concern: call qc.cleanup() (e.g., via atexit).
  • Clones need no separate handling. Session, console, and greeter clients are copy.deepcopy clones that share the base client's provider by reference (chatlas Chat.__deepcopy__ keeps provider shared), so closing the owned base client releases their underlying resources too. Closing a per-session clone would be actively harmful — it would close the shared HTTP clients out from under other live sessions.
  • Non-Shiny frameworks (Gradio/Dash/Streamlit) get no framework-specific wiring; a public, documented cleanup() is sufficient for now.

Testing

New pkg-py/tests/test_cleanup.py (7 tests): ownership flag for all three client argument forms, owned string/deferred clients closed on cleanup(), user-supplied client left open, clones covered via the shared provider, existing data-source cleanup preserved, and idempotency. Full pkg-py suite passes (588 passed; the 5 Gradio failures and playwright collection errors pre-exist on main).

ellmer 0.5.0 moves model details from Provider into a new Model class:
Provider() no longer accepts model as its second positional argument,
and Chat$new() requires a separate model argument. Gate the mock chat
client construction on whether ellmer::Model exists so tests pass on
both old and new ellmer.

Fixes #283
DuckDB 1.3.0+ and SQLite reject a CTE whose name matches a table it
references (e.g., WITH t AS (SELECT ... FROM t) SELECT * FROM t),
raising a 'circular reference' error. This broke 4 TblSqlSource tests
that use transformed tbls (CTE mode).

The fix schema-qualifies table references in the CTE body (e.g.,
FROM main.test_table instead of FROM test_table), which disambiguates
the CTE name from the physical table. The schema name is obtained via
current_schema() with a fallback to 'main' for databases that don't
support it (e.g., SQLite).
QueryChatBase.cleanup() now closes the chatlas client, but only when
querychat created it (client was None or a string spec resolved via
ChatAuto). User-supplied Chat instances are never closed -- their
lifecycle stays with the caller. Session/console/greeter clients are
deepcopy clones sharing the base provider, so closing the owned base
client releases their resources too.

Ownership is tracked via an _owns_client flag captured at construction,
since the original client argument is discarded after resolution.

Temporarily pins chatlas to the Chat.close() PR
(posit-dev/chatlas#427); must be replaced with a released version
before merging.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant