You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(py): Allow deferred chat client initialization (#205)
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>
* fix(py): require client before Shiny app setup
* refactor(py): Defer client initialization and support lazy defaults
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>
* refactor(py): Make normalize_client always return a fresh Chat
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>
* refactor(py): Rename normalize_client to create_client
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>
* refactor(py): Rename _require_client to _ensure_client
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>
* refactor(py): Use create_client for forking in client() and generate_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>
* refactor(py): Replace _client with _client_spec, eliminate state matrix
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>
* fix(py): Move _require_data_source check to public methods
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>
* docs(py): Add caller-guard comment to _create_session_client
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(py): restore deferred client behavior
* test(py): lock down shiny session-local client semantics
* test(py): refine deferred shiny client semantics tests
* fix(py): keep shiny server client overrides session-local
* fix(py): clarify server client requirement
* fix(py): align server docs and types
* test(py): cover deferred shiny client resolution helpers
* test(py): verify session-local shiny client fix
* style(py): satisfy ruff for deferred client fix
* refactor(py): remove _require_client_spec, allow None as valid default
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(py): remove stale docstring about client=None, improve readability
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(py): update tests for None-as-valid-default client behavior
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>
* docs(py): revert unnecessary explicit client= in examples and docs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style(py): fix ruff formatting in test_deferred_client
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(py): collapse _create_session_client_from_spec into _create_session_client
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(py): restore _require_data_source check in console() for error clarity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(py): remove unnecessary client= from server() docstring example
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(py): add deferred client feature to changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(r): defer client resolution, add create_session_client()
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>
* feat(r): add client parameter to $server() for session-local overrides
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>
* style(r): format deferred client changes with air
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(r): clone Chat in create_session_client() to prevent mutation
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>
* fix(py): keep deferred client resolution lazy
* `air format` (GitHub Actions)
* `devtools::document()` (GitHub Actions)
* docs(r): add deferred client feature to NEWS.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(r): mention deferred client in build vignette
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
Copy file name to clipboardExpand all lines: pkg-py/CHANGELOG.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### New features
11
+
12
+
*`QueryChat()` now supports deferred chat client initialization. Pass `client=` to `server()` to provide a session-scoped chat client, enabling use cases where API credentials are only available at session time (e.g., Posit Connect managed OAuth tokens). When no `client` is specified anywhere, querychat resolves a sensible default from the `QUERYCHAT_CLIENT` environment variable (or `"openai"`). (#205)
13
+
10
14
### Improvements
11
15
12
16
* When a custom `prompt_template` is provided that doesn't contain Mustache references to `{{schema}}`, the expensive `get_schema()` call is now skipped entirely. This allows users with large databases to avoid slow startup by providing their own prompt that includes schema information inline (or omits it). (#208)
Copy file name to clipboardExpand all lines: pkg-py/docs/build.qmd
+2Lines changed: 2 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -154,6 +154,8 @@ app = App(app_ui, server)
154
154
155
155
</details>
156
156
157
+
If your chat client also depends on session-scoped credentials, you can defer that too by passing it to `qc.server(client=...)` alongside the `data_source`.
0 commit comments