feat: Allow deferred chat client initialization#207
Merged
Conversation
When using Posit Connect managed OAuth credentials, chat client connections need access to HTTP headers in the Shiny session object, requiring creation inside the server function. Changes: - Defer client initialization when data_source=None and client=None - Add chat_client property getter/setter for setting client after init - Add client parameter to server() method for deferred pattern - Add _require_client() method for runtime checks - Update methods (client, console, generate_greeting) to require client Closes #205 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Client is no longer eagerly normalized at __init__ when not explicitly provided. Instead, _require_client(default=) resolves the client lazily: self._client takes priority, then the caller-provided default, then raises. Methods like .app() and .client() pass default=None (global default), while .server() passes the user's explicit arg (MISSING if omitted, which errors). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
normalize_client now deepcopies and clears turns internally, so callers no longer need to repeat the copy.deepcopy + set_turns([]) sequence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Better reflects that the function always returns a new, independent Chat instance (deepcopied, with turns cleared) rather than just coercing a value into canonical form. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Better reflects that the method resolves and stores the client (side effect) rather than just validating it exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…greeting() Eliminates remaining inline copy.deepcopy + set_turns([]) sequences by reusing create_client, which already handles that. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store the client specification without eager resolution. A single _create_session_client() method resolves spec -> fresh Chat -> system prompt -> tools at point-of-use. - Replace dual-role self._client with self._client_spec (just stores spec) - Delete _ensure_client, replace with _create_session_client - Rename chat_client property to client_spec (no naming collision with client()) - System prompt assigned in exactly one place (_create_session_client) - Remove MISSING sentinel from server() signature - Pass bound method to mod_server instead of lambda/bound-method-as-callable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users calling client() now see "data_source must be set before calling client()" instead of a reference to the internal _create_session_client. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
74a04d2 to
4148df2
Compare
Keep named helper functions from main for readability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove TestRequireClientSpec class and two tests that asserted None is an invalid client argument, since _require_client_spec no longer exists and None now resolves to the QUERYCHAT_CLIENT env var or "openai". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…session_client Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…arity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store the client spec (NULL/string/Chat) lazily instead of eagerly resolving it in initialize(). Add private create_session_client() as the single resolution point, and delegate $client() and $generate_greeting() to it. This enables constructing QueryChat with NULL data_source and no API key, deferring all resolution until the client is actually needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The client parameter allows per-session chat client overrides without mutating the shared client spec. This enables Posit Connect managed OAuth credentials that require session scope. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the client spec is a Chat object, as_querychat_client() returns it as-is. Without cloning, subsequent calls to $client() would mutate the same object (duplicate tool registrations, shared turn history). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cpsievert
added a commit
that referenced
this pull request
Apr 16, 2026
Integrate main's deferred client initialization (#207) and narwhals version constraint (#218) with the ggsql visualization feature branch. Extended _create_session_client with visualize_query parameter and updated stub session test to match eager client construction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
QueryChatnow supports deferred client creation in both Python and R. The chat client (chatlasChat/ ellmerChat) no longer needs to exist at construction time — you can pass it later viaserver(client=...), scoped to the current Shiny session.This unlocks Posit Connect managed OAuth workflows where credentials are only available inside the Shiny server function.
Under the hood
str | Chat | NULL) instead of eagerly resolving it at init time_create_session_client()/create_session_client()as the single resolution point that turns a spec into a fully-configured Chat with system prompt and toolsserver(client=...)captures the override in a session-local closure so it never mutates the shared spec — safe for concurrent sessionsCloses #205
Test plan
QueryChat(NULL, ...)succeeds without API key$client(),$console(),$generate_greeting()error whendata_sourceis still NULLdata_sourcelater, everything worksserver(client=...)override doesn't leak to other sessions$client()calls with a Chat spec return independent instances🤖 Generated with Claude Code