Skip to content

Commit 5676e4f

Browse files
Flatten CopilotClient options + drop unused SessionConfig TypedDicts
Make the Python public API idiomatically Pythonic, matching the conventions used by ``openai``, ``anthropic``, ``httpx``, etc. **CopilotClient: flat kw-only ctor params** The ``CopilotClientOptions`` dataclass was a port of the TS / .NET ``CopilotClientOptions`` interface and is not how a Python API client is typically configured. Python users expect ``CopilotClient(connection=..., log_level=..., github_token=...)`` rather than ``CopilotClient(CopilotClientOptions(connection=..., log_level=..., github_token=...))``. Changes: - All 12 options previously on ``CopilotClientOptions`` are now kw-only parameters on ``CopilotClient.__init__``: ``connection``, ``working_directory``, ``log_level``, ``env``, ``github_token``, ``base_directory``, ``use_logged_in_user``, ``telemetry``, ``session_fs``, ``session_idle_timeout_seconds``, ``enable_remote_sessions``, ``on_list_models``. - ``CopilotClientOptions`` is renamed to ``_CopilotClientOptions`` and is now an implementation detail used only internally to carry the resolved options around. The public ``__all__`` no longer exports it. - IDE autocomplete on ``CopilotClient(`` now lists every option directly with its type, default, and docstring. Pyright / ty catch unknown kwargs at the call site. **Drop vestigial SessionConfig / ResumeSessionConfig / SessionConfigBase** These TypedDicts mirrored the TS / .NET ``SessionConfig`` interfaces but were never used anywhere in Python — ``create_session()`` and ``resume_session()`` already take flat kw-only parameters (the idiomatic form). The TypedDicts were just unreferenced documentation noise. Updates: - README, all docs Python snippets, all 25 ``test/scenarios/**/python/main.py`` fixtures, every E2E test, every unit test now use the new flat ctor. - The test class ``TestSubprocessOptions`` (E2E) and the unit-style ``test_telemetry_config_in_subprocess_config`` test exercised the ``CopilotClientOptions`` dataclass shape itself; they're deleted since Python's type system already enforces ctor-kwarg correctness. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9786c53 commit 5676e4f

63 files changed

Lines changed: 457 additions & 982 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/auth/byok.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -369,22 +369,20 @@ const client = new CopilotClient({
369369
<summary><strong>Python</strong></summary>
370370

371371
```python
372-
from copilot import CopilotClient, CopilotClientOptions
372+
from copilot import CopilotClient
373373
from copilot.client import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits
374374

375375
client = CopilotClient(
376-
CopilotClientOptions(
377-
on_list_models=lambda: [
378-
ModelInfo(
379-
id="my-custom-model",
380-
name="My Custom Model",
381-
capabilities=ModelCapabilities(
382-
supports=ModelSupports(vision=False, reasoning_effort=False),
383-
limits=ModelLimits(max_context_window_tokens=128000),
384-
),
385-
)
386-
],
387-
),
376+
on_list_models=lambda: [
377+
ModelInfo(
378+
id="my-custom-model",
379+
name="My Custom Model",
380+
capabilities=ModelCapabilities(
381+
supports=ModelSupports(vision=False, reasoning_effort=False),
382+
limits=ModelLimits(max_context_window_tokens=128000),
383+
),
384+
)
385+
],
388386
)
389387
```
390388

docs/features/remote-sessions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ session.on("session.info", (event) => {
3838

3939
<!-- docs-validate: skip -->
4040
```python
41-
from copilot import CopilotClient, CopilotClientOptions
41+
from copilot import CopilotClient
4242

43-
client = CopilotClient(CopilotClientOptions(enable_remote_sessions=True))
43+
client = CopilotClient(enable_remote_sessions=True)
4444
session = await client.create_session(
4545
working_directory="/path/to/github-repo",
4646
on_permission_request=lambda req: {"allowed": True},

docs/observability/opentelemetry.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ const client = new CopilotClient({
2727

2828
<!-- docs-validate: skip -->
2929
```python
30-
from copilot import CopilotClient, CopilotClientOptions
30+
from copilot import CopilotClient
3131

32-
client = CopilotClient(CopilotClientOptions(
32+
client = CopilotClient(
3333
telemetry={
3434
"otlp_endpoint": "http://localhost:4318",
3535
},
36-
))
36+
)
3737
```
3838

3939
</details>

docs/setup/backend-services.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ res.json({ content: response?.data.content });
144144
<summary><strong>Python</strong></summary>
145145

146146
```python
147-
from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection
147+
from copilot import CopilotClient, RuntimeConnection
148148
from copilot.session import PermissionHandler
149149

150-
client = CopilotClient(CopilotClientOptions(
150+
client = CopilotClient(
151151
connection=RuntimeConnection.for_uri("localhost:4321"),
152-
))
152+
)
153153
await client.start()
154154

155155
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", session_id=f"user-{user_id}-{int(time.time())}")

docs/troubleshooting/debugging.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ const client = new CopilotClient({
3232
<summary><strong>Python</strong></summary>
3333

3434
```python
35-
from copilot import CopilotClient, CopilotClientOptions
35+
from copilot import CopilotClient
3636

37-
client = CopilotClient(CopilotClientOptions(log_level="debug"))
37+
client = CopilotClient(log_level="debug")
3838
```
3939

4040
</details>

python/README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,20 @@ async with CopilotClient() as client:
133133
> **Note:** For manual lifecycle management, see [Manual Resource Management](#manual-resource-management) above.
134134
135135
```python
136-
from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection
136+
from copilot import CopilotClient, RuntimeConnection
137137

138138
# Connect to an existing CLI server
139-
client = CopilotClient(
140-
CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000"))
141-
)
139+
client = CopilotClient(connection=RuntimeConnection.for_uri("localhost:3000"))
142140
```
143141

144142
**CopilotClient Constructor:**
145143

146144
```python
147-
CopilotClient() # spawn the bundled runtime with defaults
148-
CopilotClient(CopilotClientOptions(...)) # customise via options
145+
CopilotClient() # spawn the bundled runtime with defaults
146+
CopilotClient(connection=..., log_level="debug", github_token=..., ...)
149147
```
150148

151-
**CopilotClientOptions** — configure the client:
149+
All options are kw-only parameters:
152150

153151
- `connection` (RuntimeConnection | None): How to reach the runtime. Use
154152
`RuntimeConnection.for_stdio(...)`, `RuntimeConnection.for_tcp(...)`, or
@@ -161,6 +159,7 @@ CopilotClient(CopilotClientOptions(...)) # customise via options
161159
- `use_logged_in_user` (bool | None): Whether to use logged-in user for authentication (default: True, but False when `github_token` is provided).
162160
- `telemetry` (dict | None): OpenTelemetry configuration for the CLI process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
163161
- `enable_remote_sessions` (bool): Enable remote/cloud session support (default: False).
162+
- `on_list_models` (callable | None): Custom handler for `list_models()`. When provided, the handler is called instead of querying the runtime.
164163

165164
**RuntimeConnection variants:**
166165

@@ -531,13 +530,13 @@ async with await client.create_session(
531530
The SDK supports OpenTelemetry for distributed tracing. Provide a `telemetry` config to enable trace export and automatic W3C Trace Context propagation.
532531

533532
```python
534-
from copilot import CopilotClient, CopilotClientOptions
533+
from copilot import CopilotClient
535534

536-
client = CopilotClient(CopilotClientOptions(
535+
client = CopilotClient(
537536
telemetry={
538537
"otlp_endpoint": "http://localhost:4318",
539538
},
540-
))
539+
)
541540
```
542541

543542
**TelemetryConfig options:**

python/copilot/__init__.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
CloudSessionOptions,
1010
CloudSessionRepository,
1111
CopilotClient,
12-
CopilotClientOptions,
1312
GetAuthStatusResponse,
1413
GetStatusResponse,
1514
LogLevel,
@@ -87,10 +86,7 @@
8786
PreToolUseHookInput,
8887
PreToolUseHookOutput,
8988
ProviderConfig,
90-
ResumeSessionConfig,
9189
SessionCapabilities,
92-
SessionConfig,
93-
SessionConfigBase,
9490
SessionEndHandler,
9591
SessionEndHookInput,
9692
SessionEndHookOutput,
@@ -140,7 +136,6 @@
140136
"CommandContext",
141137
"CommandDefinition",
142138
"CopilotClient",
143-
"CopilotClientOptions",
144139
"CopilotSession",
145140
"CreateSessionFsHandler",
146141
"ElicitationContext",
@@ -188,12 +183,9 @@
188183
"PreToolUseHookOutput",
189184
"ProviderConfig",
190185
"RemoteSessionMode",
191-
"ResumeSessionConfig",
192186
"RuntimeConnection",
193187
"SessionBackgroundEvent",
194188
"SessionCapabilities",
195-
"SessionConfig",
196-
"SessionConfigBase",
197189
"SessionContext",
198190
"SessionCreatedEvent",
199191
"SessionDeletedEvent",

python/copilot/client.py

Lines changed: 72 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,7 @@ class RuntimeConnection:
165165
166166
Example:
167167
>>> CopilotClient() # default: stdio with the bundled runtime
168-
>>> CopilotClient(
169-
... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000"))
170-
... )
168+
>>> CopilotClient(connection=RuntimeConnection.for_uri("localhost:3000"))
171169
"""
172170

173171
@staticmethod
@@ -282,73 +280,25 @@ class UriRuntimeConnection(RuntimeConnection):
282280

283281

284282
@dataclass
285-
class CopilotClientOptions:
286-
"""Configuration options for a :class:`CopilotClient`.
283+
class _CopilotClientOptions:
284+
"""Internal configuration carrier used by :class:`CopilotClient`.
287285
288-
All process-management options (``working_directory``, ``log_level``,
289-
``env``, ``github_token``, …) apply only when the SDK spawns the runtime
290-
(stdio / tcp connections). They are ignored when connecting to an
291-
existing runtime via :meth:`RuntimeConnection.uri`.
286+
This is not part of the public API: ``CopilotClient`` accepts all of
287+
these options as keyword arguments directly.
292288
"""
293289

294290
connection: RuntimeConnection | None = None
295-
"""How to reach the runtime.
296-
297-
Defaults to :meth:`RuntimeConnection.stdio` with the bundled binary.
298-
"""
299-
300291
working_directory: str | None = None
301-
"""Working directory for the runtime process. ``None`` uses the current directory."""
302-
303292
log_level: LogLevel = "info"
304-
"""Log level for the runtime process."""
305-
306293
env: dict[str, str] | None = None
307-
"""Environment variables for the runtime process. ``None`` inherits the current env."""
308-
309294
github_token: str | None = None
310-
"""GitHub token for authentication. Takes priority over other auth methods."""
311-
312295
base_directory: str | None = None
313-
"""Base directory for Copilot data (session state, config, etc.).
314-
315-
Sets the ``COPILOT_HOME`` environment variable on the spawned runtime.
316-
When ``None``, the runtime defaults to ``~/.copilot``.
317-
"""
318-
319296
use_logged_in_user: bool | None = None
320-
"""Use the logged-in user for authentication.
321-
322-
``None`` (default) resolves to ``True`` unless ``github_token`` is set.
323-
"""
324-
325297
telemetry: TelemetryConfig | None = None
326-
"""OpenTelemetry configuration. Providing this enables telemetry."""
327-
328298
session_fs: SessionFsConfig | None = None
329-
"""Connection-level session filesystem provider configuration."""
330-
331299
session_idle_timeout_seconds: int | None = None
332-
"""Server-wide session idle timeout in seconds.
333-
334-
Sessions without activity for this duration are automatically cleaned up.
335-
Set to ``None`` or ``0`` to disable.
336-
"""
337-
338300
enable_remote_sessions: bool = False
339-
"""Enable remote session support (Mission Control integration).
340-
341-
When ``True``, sessions in a GitHub repository working directory are
342-
accessible from GitHub web and mobile.
343-
"""
344-
345301
on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None
346-
"""Custom handler for :meth:`CopilotClient.list_models`.
347-
348-
When provided, the handler is called instead of querying the runtime
349-
server. Matches the ``onListModels`` / ``OnListModels`` option in the
350-
TypeScript and .NET SDKs.
351-
"""
352302

353303

354304
# ============================================================================
@@ -1082,46 +1032,100 @@ class CopilotClient:
10821032
10831033
>>> # Or connect to an existing server
10841034
>>> client = CopilotClient(
1085-
... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000"))
1035+
... connection=RuntimeConnection.for_uri("localhost:3000"),
10861036
... )
10871037
"""
10881038

10891039
def __init__(
10901040
self,
1091-
options: CopilotClientOptions | None = None,
1041+
*,
1042+
connection: RuntimeConnection | None = None,
1043+
working_directory: str | None = None,
1044+
log_level: LogLevel = "info",
1045+
env: dict[str, str] | None = None,
1046+
github_token: str | None = None,
1047+
base_directory: str | None = None,
1048+
use_logged_in_user: bool | None = None,
1049+
telemetry: TelemetryConfig | None = None,
1050+
session_fs: SessionFsConfig | None = None,
1051+
session_idle_timeout_seconds: int | None = None,
1052+
enable_remote_sessions: bool = False,
1053+
on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None,
10921054
):
10931055
"""
10941056
Initialize a new CopilotClient.
10951057
1058+
All process-management options (``working_directory``, ``log_level``,
1059+
``env``, ``github_token``, …) apply only when the SDK spawns the runtime
1060+
(stdio / tcp connections). They are ignored when connecting to an
1061+
existing runtime via :meth:`RuntimeConnection.for_uri`.
1062+
10961063
Args:
1097-
options: Client configuration. Defaults to ``CopilotClientOptions()``
1098-
with a default :meth:`RuntimeConnection.for_stdio` connection using
1099-
the bundled runtime binary.
1064+
connection: How to reach the runtime. Defaults to
1065+
:meth:`RuntimeConnection.for_stdio` with the bundled binary.
1066+
working_directory: Working directory for the runtime process.
1067+
``None`` uses the current directory.
1068+
log_level: Log level for the runtime process. Defaults to ``"info"``.
1069+
env: Environment variables for the runtime process. ``None`` inherits
1070+
the current env.
1071+
github_token: GitHub token for authentication. Takes priority over
1072+
other auth methods.
1073+
base_directory: Base directory for Copilot data (session state,
1074+
config, etc.). Sets the ``COPILOT_HOME`` environment variable on
1075+
the spawned runtime. When ``None``, the runtime defaults to
1076+
``~/.copilot``.
1077+
use_logged_in_user: Use the logged-in user for authentication.
1078+
``None`` (default) resolves to ``True`` unless ``github_token``
1079+
is set.
1080+
telemetry: OpenTelemetry configuration. Providing this enables
1081+
telemetry.
1082+
session_fs: Connection-level session filesystem provider
1083+
configuration.
1084+
session_idle_timeout_seconds: Server-wide session idle timeout in
1085+
seconds. Sessions without activity for this duration are
1086+
automatically cleaned up. Set to ``None`` or ``0`` to disable.
1087+
enable_remote_sessions: Enable remote session support (Mission
1088+
Control integration). When ``True``, sessions in a GitHub
1089+
repository working directory are accessible from GitHub web
1090+
and mobile.
1091+
on_list_models: Custom handler for :meth:`list_models`. When
1092+
provided, the handler is called instead of querying the runtime
1093+
server.
11001094
11011095
Example:
11021096
>>> # Default — spawns runtime using stdio with the bundled binary
11031097
>>> client = CopilotClient()
11041098
>>>
11051099
>>> # Connect to an existing runtime
11061100
>>> client = CopilotClient(
1107-
... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000"))
1101+
... connection=RuntimeConnection.for_uri("localhost:3000"),
11081102
... )
11091103
>>>
11101104
>>> # Custom runtime path with specific log level
11111105
>>> client = CopilotClient(
1112-
... CopilotClientOptions(
1113-
... connection=RuntimeConnection.for_stdio(path="/usr/local/bin/copilot"),
1114-
... log_level="debug",
1115-
... )
1106+
... connection=RuntimeConnection.for_stdio(path="/usr/local/bin/copilot"),
1107+
... log_level="debug",
11161108
... )
11171109
"""
1118-
if options is None:
1119-
options = CopilotClientOptions()
1110+
options = _CopilotClientOptions(
1111+
connection=connection,
1112+
working_directory=working_directory,
1113+
log_level=log_level,
1114+
env=env,
1115+
github_token=github_token,
1116+
base_directory=base_directory,
1117+
use_logged_in_user=use_logged_in_user,
1118+
telemetry=telemetry,
1119+
session_fs=session_fs,
1120+
session_idle_timeout_seconds=session_idle_timeout_seconds,
1121+
enable_remote_sessions=enable_remote_sessions,
1122+
on_list_models=on_list_models,
1123+
)
11201124
connection = (
11211125
options.connection if options.connection is not None else RuntimeConnection.for_stdio()
11221126
)
11231127

1124-
self._options: CopilotClientOptions = options
1128+
self._options: _CopilotClientOptions = options
11251129
self._connection: RuntimeConnection = connection
11261130
self._on_list_models = options.on_list_models
11271131

0 commit comments

Comments
 (0)