Skip to content

Commit 8ab52ed

Browse files
Merge branch 'main' into fix/keyvault-certificates-san-ip-uri-validator
2 parents 25a65b6 + 496b2b7 commit 8ab52ed

44 files changed

Lines changed: 3482 additions & 227 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
parameters:
2+
- name: ArtifactPath
3+
type: string
4+
default: $(Build.ArtifactStagingDirectory)/PackageInfo
5+
- name: Repo
6+
type: string
7+
default: $(Build.Repository.Name)
8+
- name: SdkTypes
9+
type: object
10+
default:
11+
- client
12+
- compat
13+
- data
14+
- functions
15+
- datamovement
16+
17+
steps:
18+
- template: /eng/common/pipelines/templates/steps/install-azsdk-cli.yml
19+
parameters:
20+
Condition: and(succeeded(), ne(variables['Skip.VerifyCodeowners'], 'true'))
21+
22+
- task: PowerShell@2
23+
displayName: Verify Codeowners
24+
condition: and(succeeded(), ne(variables['Skip.VerifyCodeowners'], 'true'))
25+
inputs:
26+
pwsh: true
27+
filePath: $(Build.SourcesDirectory)/eng/common/scripts/Test-CodeownersForArtifacts.ps1
28+
arguments: >-
29+
-AzsdkPath '$(AZSDK)'
30+
-PackageInfoDirectory '${{ parameters.ArtifactPath }}'
31+
-SdkTypes ${{ join(',', parameters.SdkTypes) }}
32+
-Repo '${{ parameters.Repo }}'
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
[CmdletBinding()]
2+
param(
3+
[string] $AzsdkPath,
4+
[string] $PackageInfoDirectory,
5+
[array] $SdkTypes,
6+
[string] $Repo
7+
)
8+
9+
. "$PSScriptRoot/common.ps1"
10+
11+
Set-StrictMode -Version 3
12+
$ErrorActionPreference = 'Stop'
13+
14+
$failedPackages = @()
15+
16+
foreach ($pkgPropertiesFile in Get-ChildItem -Path $PackageInfoDirectory -Filter '*.json' -File) {
17+
$pkgProperties = Get-Content -Raw -Path $pkgPropertiesFile | ConvertFrom-Json
18+
if ($SdkTypes -notcontains $pkgProperties.SdkType) {
19+
Write-Host "Skipping package: $($pkgProperties.Name) $($pkgProperties.DirectoryPath) because its SdkType '$($pkgProperties.SdkType)' is not in the list of SdkTypes to validate."
20+
continue
21+
}
22+
23+
Write-Host "Validating codeowners for package: $($pkgProperties.Name) $($pkgProperties.DirectoryPath)"
24+
25+
if (!$pkgProperties.ReleaseStatus) {
26+
LogError "Package $($pkgProperties.Name) at $($pkgProperties.DirectoryPath) is missing a ReleaseStatus property."
27+
$failedPackages += $pkgProperties.DirectoryPath
28+
continue
29+
}
30+
31+
# Validate packages with a release date (intended to release)
32+
if ($pkgProperties.ReleaseStatus -ne "Unreleased") {
33+
$output = & $AzsdkPath config codeowners check-package `
34+
--directory-path $pkgProperties.DirectoryPath `
35+
--repo $Repo `
36+
--output json 2>&1
37+
38+
if ($LASTEXITCODE) {
39+
LogError "Codeowners validation failed for package: $($pkgProperties.DirectoryPath)"
40+
$output | Write-Host
41+
$failedPackages += $pkgProperties.DirectoryPath
42+
} else {
43+
Write-Host " Codeowners validation succeeded for package: $($pkgProperties.DirectoryPath)"
44+
}
45+
} else {
46+
Write-Host " Skipping CODEOWNERS validation, package is not intended to release."
47+
}
48+
}
49+
50+
if ($failedPackages.Count -gt 0) {
51+
Write-Host ""
52+
Write-Host "Failed Packages:"
53+
foreach ($directoryPath in $failedPackages) {
54+
LogError " - $directoryPath does not have sufficient code owners coverage"
55+
}
56+
LogError "Codeowners validation failed for one or more packages. See http://aka.ms/azsdk/codeowners for instructions to fix the issue."
57+
exit 1
58+
}
59+
exit 0

scripts/breaking_changes_checker/detect_breaking_changes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -606,10 +606,11 @@ def main(
606606
packages = [f"{package_name}=={version}", "jsondiff==1.2.0"]
607607
with create_venv_with_package(packages) as venv:
608608
subprocess.check_call(
609-
[venv.env_exe, "-m", "pip", "install", "-r", os.path.join(pkg_dir, "dev_requirements.txt")]
609+
[venv.env_exe, "-m", "pip", "install", "-r", os.path.join(pkg_dir, "dev_requirements.txt")],
610+
cwd=pkg_dir,
610611
)
611612
_LOGGER.info(f"Installed version {version} of {package_name} in a venv")
612-
args = [venv.env_exe, __file__, "-t", package_name, "-m", target_module, "--in-venv", "true", "-s", version]
613+
args = [venv.env_exe, __file__, "-t", pkg_dir, "-m", target_module, "--in-venv", "true", "-s", version]
613614
try:
614615
subprocess.check_call(args)
615616
except subprocess.CalledProcessError:
@@ -619,12 +620,12 @@ def main(
619620
public_api = build_library_report(target_module)
620621

621622
if in_venv:
622-
with open("stable.json", "w") as fd:
623+
with open(os.path.join(pkg_dir, "stable.json"), "w") as fd:
623624
json.dump(public_api, fd, indent=2)
624625
_LOGGER.info("stable.json is written.")
625626
return
626627

627-
with open("current.json", "w") as fd:
628+
with open(os.path.join(pkg_dir, "current.json"), "w") as fd:
628629
json.dump(public_api, fd, indent=2)
629630
_LOGGER.info("current.json is written.")
630631

sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Release History
22

3+
## 2.0.0b2 (2026-04-17)
4+
5+
### Features Added
6+
7+
- Startup configuration logging — `AgentServerHost` lifespan now emits three INFO-level log lines at startup: platform environment (agent name, version, port, session ID, SSE keep-alive), connectivity (project endpoint and OTLP endpoint masked to scheme://host, Application Insights configured flag), and host options (shutdown timeout, registered protocols). Sensitive values (Application Insights connection string) are never logged.
8+
- `InboundRequestLoggingMiddleware` — pure-ASGI middleware wired automatically by `AgentServerHost` that logs every inbound HTTP request. Logs method, path (no query string), status code, duration in milliseconds, and correlation headers (`x-request-id`, `x-ms-client-request-id`). Status codes >= 400 are logged at WARNING; unhandled exceptions are logged as status 500 at WARNING. OpenTelemetry trace ID is included when an active trace exists.
9+
310
## 2.0.0b1 (2026-04-14)
411

512
This is a major architectural rewrite. The package has been redesigned as a lightweight hosting

sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ._base import AgentServerHost
2727
from ._config import AgentConfig
2828
from ._errors import create_error_response
29+
from ._middleware import InboundRequestLoggingMiddleware
2930
from ._server_version import build_server_version
3031
from ._tracing import (
3132
configure_observability,
@@ -41,6 +42,7 @@
4142
__all__ = [
4243
"AgentConfig",
4344
"AgentServerHost",
45+
"InboundRequestLoggingMiddleware",
4446
"build_server_version",
4547
"configure_observability",
4648
"create_error_response",

sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
import logging
77
import os
88
import signal
9-
from collections.abc import AsyncGenerator, AsyncIterable, AsyncIterator, Awaitable, Callable # pylint: disable=import-error
9+
import urllib.parse
10+
from collections.abc import ( # pylint: disable=import-error
11+
AsyncGenerator,
12+
AsyncIterable,
13+
AsyncIterator,
14+
Awaitable,
15+
Callable,
16+
)
1017
from typing import Any, MutableMapping, Optional, Union
1118

1219
from starlette.applications import Starlette
@@ -17,6 +24,7 @@
1724
from starlette.types import ASGIApp, Receive, Scope, Send
1825

1926
from . import _config, _tracing
27+
from ._middleware import InboundRequestLoggingMiddleware
2028
from ._server_version import build_server_version
2129
from ._version import VERSION as _CORE_VERSION
2230

@@ -25,6 +33,35 @@
2533
# Pre-built health-check response to avoid per-request allocation.
2634
_HEALTHY_BODY = b'{"status":"healthy"}'
2735

36+
_NOT_SET = "(not set)"
37+
38+
39+
def _mask_uri(uri: str) -> str:
40+
"""Return only the scheme and host of a URI, hiding path/query/credentials.
41+
42+
Returns ``"(not set)"`` for empty or whitespace-only values, or
43+
``"(redacted)"`` when the URI cannot be parsed into scheme + host.
44+
45+
:param uri: The URI to mask.
46+
:type uri: str
47+
:return: ``"scheme://host[:port]"``, ``"(not set)"``, or ``"(redacted)"``.
48+
:rtype: str
49+
"""
50+
stripped = uri.strip() if uri else ""
51+
if not stripped:
52+
return _NOT_SET
53+
try:
54+
parsed = urllib.parse.urlparse(stripped)
55+
scheme = parsed.scheme or ""
56+
host = parsed.hostname or ""
57+
if scheme and host:
58+
port_suffix = f":{parsed.port}" if parsed.port else ""
59+
return f"{scheme}://{host}{port_suffix}"
60+
# Best-effort: if parsing fails to extract components, redact entirely
61+
return "(redacted)"
62+
except Exception: # pylint: disable=broad-exception-caught
63+
return "(redacted)"
64+
2865

2966
class _PlatformHeaderMiddleware:
3067
"""Pure-ASGI middleware that adds ``x-platform-server`` header to responses.
@@ -176,6 +213,32 @@ def __init__(
176213
@contextlib.asynccontextmanager
177214
async def _lifespan(_app: Starlette) -> AsyncGenerator[None, None]: # noqa: RUF029
178215
logger.info("AgentServerHost started")
216+
217+
# --- Startup configuration logging ---
218+
cfg = self.config
219+
logger.info(
220+
"Platform environment: is_hosted=%s, agent_name=%s, agent_version=%s, "
221+
"port=%s, session_id=%s, sse_keepalive_interval=%s",
222+
cfg.is_hosted,
223+
cfg.agent_name or _NOT_SET,
224+
cfg.agent_version or _NOT_SET,
225+
cfg.port,
226+
cfg.session_id or _NOT_SET,
227+
cfg.sse_keepalive_interval if cfg.sse_keepalive_interval > 0 else "disabled",
228+
)
229+
logger.info(
230+
"Connectivity: project_endpoint=%s, otlp_endpoint=%s, appinsights_configured=%s",
231+
_mask_uri(cfg.project_endpoint),
232+
_mask_uri(cfg.otlp_endpoint),
233+
bool(cfg.appinsights_connection_string),
234+
)
235+
protocols = ", ".join(self._server_version_segments) if self._server_version_segments else _NOT_SET
236+
logger.info(
237+
"Host options: shutdown_timeout=%ss, protocols=%s",
238+
self._graceful_shutdown_timeout,
239+
protocols,
240+
)
241+
179242
yield
180243

181244
# --- SHUTDOWN: runs once when the server is stopping ---
@@ -210,7 +273,11 @@ async def _lifespan(_app: Starlette) -> AsyncGenerator[None, None]: # noqa: RUF
210273
routes=all_routes,
211274
lifespan=_lifespan,
212275
middleware=[
213-
Middleware(_PlatformHeaderMiddleware, get_server_version=self._build_server_version),
276+
Middleware(InboundRequestLoggingMiddleware), # type: ignore[arg-type]
277+
Middleware( # type: ignore[arg-type]
278+
_PlatformHeaderMiddleware,
279+
get_server_version=self._build_server_version,
280+
),
214281
],
215282
**kwargs,
216283
)

sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""
1616
import os
1717
from typing import Optional
18+
1819
from typing_extensions import Self
1920

2021
# ======================================================================

0 commit comments

Comments
 (0)