Python: improve .env handling and observability samples (#4032)

* Python: improve .env precedence and observability samples

- Switch load_settings to explicit precedence: overrides -> explicit .env -> environment -> defaults\n- Raise when env_file_path is provided but missing\n- Update settings docs and tests for new behavior\n- Refresh observability samples and README guidance for env loading options\n\nCloses #3864\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fixed some imports

* Fix load_settings CI regressions

Allow explicit env_file_path values that exist but are not regular files (for example /dev/null) by checking path existence before dotenv parsing, and restore a dict accumulator with typed return cast to satisfy mypy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Avoid implicit dotenv in observability

Only load dotenv in observability helpers when env_file_path is explicitly provided, and remove test os.devnull workarounds that are no longer necessary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Eduard van Valkenburg
2026-02-18 12:18:52 +01:00
committed by GitHub
Unverified
parent f900febb6f
commit 534e5f5bf7
41 changed files with 250 additions and 221 deletions
@@ -2,8 +2,7 @@
import importlib.metadata
from ._agent import GitHubCopilotAgent, GitHubCopilotOptions
from ._settings import GitHubCopilotSettings
from ._agent import GitHubCopilotAgent, GitHubCopilotOptions, GitHubCopilotSettings
try:
__version__ = importlib.metadata.version(__name__)
@@ -40,8 +40,6 @@ from copilot.types import (
)
from copilot.types import Tool as CopilotTool
from ._settings import GitHubCopilotSettings
if sys.version_info >= (3, 13):
from typing import TypeVar
else:
@@ -57,6 +55,30 @@ PermissionHandlerType = Callable[[PermissionRequest, dict[str, str]], Permission
logger = logging.getLogger("agent_framework.github_copilot")
class GitHubCopilotSettings(TypedDict, total=False):
"""GitHub Copilot model settings.
Settings are resolved in this order: explicit keyword arguments, values from an
explicitly provided .env file, then environment variables with the prefix
'GITHUB_COPILOT_'.
Keys:
cli_path: Path to the Copilot CLI executable.
Can be set via environment variable GITHUB_COPILOT_CLI_PATH.
model: Model to use (e.g., "gpt-5", "claude-sonnet-4").
Can be set via environment variable GITHUB_COPILOT_MODEL.
timeout: Request timeout in seconds.
Can be set via environment variable GITHUB_COPILOT_TIMEOUT.
log_level: CLI log level.
Can be set via environment variable GITHUB_COPILOT_LOG_LEVEL.
"""
cli_path: str | None
model: str | None
timeout: float | None
log_level: str | None
class GitHubCopilotOptions(TypedDict, total=False):
"""GitHub Copilot-specific options."""
@@ -1,27 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from typing import TypedDict
class GitHubCopilotSettings(TypedDict, total=False):
"""GitHub Copilot model settings.
The settings are first loaded from environment variables with the prefix 'GITHUB_COPILOT_'.
If the environment variables are not found, the settings can be loaded from a .env file
with the encoding 'utf-8'.
Keys:
cli_path: Path to the Copilot CLI executable.
Can be set via environment variable GITHUB_COPILOT_CLI_PATH.
model: Model to use (e.g., "gpt-5", "claude-sonnet-4").
Can be set via environment variable GITHUB_COPILOT_MODEL.
timeout: Request timeout in seconds.
Can be set via environment variable GITHUB_COPILOT_TIMEOUT.
log_level: CLI log level.
Can be set via environment variable GITHUB_COPILOT_LOG_LEVEL.
"""
cli_path: str | None
model: str | None
timeout: float | None
log_level: str | None