Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Azure/azure-sdk-for-python
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 66c7d3d52474d7aba45cb93db3f86f8a4bf34632
Choose a base ref
..
head repository: Azure/azure-sdk-for-python
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d8a90dcaafa794f5afadc47e32d231448d643c77
Choose a head ref
Showing 627 changed files with 8,502 additions and 94,992 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -122,7 +122,7 @@

# ServiceLabel: %Communication - Phone Numbers
# PRLabel: %Communication - Phone Numbers
/sdk/communication/azure-communication-phonenumbers/ @miguhern @whisper6284 @danielav7
/sdk/communication/azure-communication-phonenumbers/ @Arazan @whisper6284 @danielortega-msft @sofiar-msft

# ServiceLabel: %Communication - Rooms
# PRLabel: %Communication - Rooms
4 changes: 2 additions & 2 deletions .github/workflows/event-processor.yml
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ jobs:
run: >
dotnet tool install
Azure.Sdk.Tools.GitHubEventProcessor
--version 1.0.0-dev.20241206.2
--version 1.0.0-dev.20250314.4
--add-source https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json
--global
shell: bash
@@ -118,7 +118,7 @@ jobs:
run: >
dotnet tool install
Azure.Sdk.Tools.GitHubEventProcessor
--version 1.0.0-dev.20241206.2
--version 1.0.0-dev.20250314.4
--add-source https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json
--global
shell: bash
2 changes: 1 addition & 1 deletion .github/workflows/scheduled-event-processor.yml
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ jobs:
run: >
dotnet tool install
Azure.Sdk.Tools.GitHubEventProcessor
--version 1.0.0-dev.20241206.2
--version 1.0.0-dev.20250314.4
--add-source https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json
--global
shell: bash
12 changes: 12 additions & 0 deletions eng/common/TestResources/New-TestResources.ps1
Original file line number Diff line number Diff line change
@@ -194,6 +194,18 @@ try {
-serviceDirectoryName $serviceName `
-CI $CI

if ($wellKnownTMETenants.Contains($TenantId)) {
# Add a prefix to the resource group name to avoid flagging the usages of local auth
# See details at https://eng.ms/docs/products/onecert-certificates-key-vault-and-dsms/key-vault-dsms/certandsecretmngmt/credfreefaqs#how-can-i-disable-s360-reporting-when-testing-customer-facing-3p-features-that-depend-on-use-of-unsafe-local-auth
$ResourceGroupName = "SSS3PT_" + $ResourceGroupName
}

if ($ResourceGroupName.Length -gt 90) {
# See limits at https://docs.microsoft.com/azure/architecture/best-practices/resource-naming
Write-Warning -Message "Resource group name '$ResourceGroupName' is too long. So pruning it to be the first 90 characters."
$ResourceGroupName = $ResourceGroupName.Substring(0, 90)
}

# Make sure pre- and post-scripts are passed formerly required arguments.
$PSBoundParameters['BaseName'] = $BaseName

4 changes: 2 additions & 2 deletions eng/common/pipelines/templates/jobs/prepare-pipelines.yml
Original file line number Diff line number Diff line change
@@ -106,8 +106,8 @@ jobs:
}
"rust" {
$generatePublicCIPipeline = 'false'
$internalVariableGroups = '$(AzureSDK_CratesIo_Release_Pipeline_Secrets) $(Release_Secrets_for_GitHub) $(APIReview_AutoCreate_Configurations)'
$testVariableGroups = '$(Secrets_for_Resource_Provisioner)'
$internalVariableGroups = '$(AzureSDK_CratesIo_Release_Pipeline_Secrets) $(Release_Secrets_for_GitHub) $(APIReview_AutoCreate_Configurations) $(Secrets_for_Resource_Provisioner)'
$generateUnifiedWeekly = 'true'
}
"net" {
$internalVariableGroups = '$(AzureSDK_Nuget_Release_Pipeline_Secrets) $(Release_Secrets_for_GitHub) $(APIReview_AutoCreate_Configurations)'
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ steps:
filePath: ${{ parameters.RepoRoot }}/eng/common/scripts/Detect-Api-Changes.ps1
arguments: >
-ArtifactPath ${{parameters.ArtifactPath}}
-CommitSha '$(Build.SourceVersion)'
-CommitSha '$(System.PullRequest.SourceCommitId)'
-BuildId $(Build.BuildId)
-PullRequestNumber $(System.PullRequest.PullRequestNumber)
-RepoFullName $(Build.Repository.Name)
4 changes: 2 additions & 2 deletions eng/common/scripts/Helpers/Resource-Helpers.ps1
Original file line number Diff line number Diff line change
@@ -223,8 +223,8 @@ function Remove-WormStorageAccounts() {
# DO NOT REMOVE THIS
# We call this script from live test pipelines as well, and a string mismatch/error could blow away
# some static storage accounts we rely on
if (!$groupPrefix -or ($CI -and !$GroupPrefix.StartsWith('rg-'))) {
throw "The -GroupPrefix parameter must not be empty, or must start with 'rg-' in CI contexts"
if (!$groupPrefix -or ($CI -and (!$GroupPrefix.StartsWith('rg-') -and !$GroupPrefix.StartsWith('SSS3PT_rg-')))) {
throw "The -GroupPrefix parameter must not be empty, or must start with 'rg-' or 'SSS3PT_rg-' in CI contexts"
}

$groups = Get-AzResourceGroup | Where-Object { $_.ResourceGroupName.StartsWith($GroupPrefix) } | Where-Object { $_.ProvisioningState -ne 'Deleting' }
2 changes: 1 addition & 1 deletion eng/common/scripts/Package-Properties.ps1
Original file line number Diff line number Diff line change
@@ -266,7 +266,7 @@ function Update-TargetedFilesForExclude([string[]]$TargetedFiles, [string[]]$Exc
foreach ($file in $TargetedFiles) {
$shouldExclude = $false
foreach ($exclude in $ExcludePaths) {
if (!$file.StartsWith($exclude,'CurrentCultureIgnoreCase')) {
if ($file.StartsWith($exclude,'CurrentCultureIgnoreCase')) {
$shouldExclude = $true
break
}
8 changes: 4 additions & 4 deletions eng/common/scripts/job-matrix/Create-PrJobMatrix.ps1
Original file line number Diff line number Diff line change
@@ -180,7 +180,7 @@ function GeneratePRMatrixForBatch {
}

if ($batchSuffixNecessary) {
$outputItem["name"] = $outputItem["name"] + "$batchNamePrefix$batchCounter"
$outputItem["name"] = $outputItem["name"] + "_$batchNamePrefix$batchCounter"
}

$OverallResult += $outputItem
@@ -205,7 +205,7 @@ function GeneratePRMatrixForBatch {
}

if ($batchSuffixNecessary) {
$outputItem["name"] = $outputItem["name"] + "_$batchNamePrefix$batchCounter"
$outputItem["name"] = $outputItem["name"] + "_$batchNamePrefix$batchCounter"
}
# now we need to take an item from the front of the matrix results, clone it, and add it to the back of the matrix results
# we will add the cloned version to OverallResult
@@ -253,14 +253,14 @@ if ($directPackages) {
foreach($artifact in $directPackages) {
Write-Host "-> $($artifact.ArtifactName)"
}
$OverallResult += GeneratePRMatrixForBatch -Packages $directPackages
$OverallResult += (GeneratePRMatrixForBatch -Packages $directPackages) ?? @()
}
if ($indirectPackages) {
Write-Host "Discovered $($indirectPackages.Length) indirect packages"
foreach($artifact in $indirectPackages) {
Write-Host "-> $($artifact.ArtifactName)"
}
$OverallResult += GeneratePRMatrixForBatch -Packages $indirectPackages -FullSparseMatrix (-not $SparseIndirect)
$OverallResult += (GeneratePRMatrixForBatch -Packages $indirectPackages -FullSparseMatrix (-not $SparseIndirect)) ?? @()
}
$serialized = SerializePipelineMatrix $OverallResult

48 changes: 24 additions & 24 deletions eng/scripts/get_package_properties.py
Original file line number Diff line number Diff line change
@@ -8,34 +8,34 @@

additional_pr_triggers: Dict[str, List[str]] = {
"azure-core":[
os.path.join("/sdk", "servicebus", "azure-servicebus"),
os.path.join("/sdk", "eventhub", "azure-eventhub"),
os.path.join("/sdk", "tables", "azure-data-tables"),
os.path.join("/sdk", "appconfiguration", "azure-appconfiguration"),
os.path.join("/sdk", "keyvault", "azure-keyvault-keys"),
os.path.join("/sdk", "identity", "azure-identity"),
os.path.join("/sdk", "core", "azure-mgmt-core"),
os.path.join("/sdk", "core", "azure-core-experimental"),
os.path.join("/sdk", "core", "azure-core-tracing-opentelemetry"),
os.path.join("/sdk", "core", "azure-core-tracing-opencensus"),
"/sdk/servicebus/azure-servicebus",
"/sdk/eventhub/azure-eventhub",
"/sdk/tables/azure-data-tables",
"/sdk/appconfiguration/azure-appconfiguration",
"/sdk/keyvault/azure-keyvault-keys",
"/sdk/identity/azure-identity",
"/sdk/core/azure-mgmt-core",
"/sdk/core/azure-core-experimental",
"/sdk/core/azure-core-tracing-opentelemetry",
"/sdk/core/azure-core-tracing-opencensus",
# todo: this currently won't be included, as azure-cosmos needs some special construction
# related to windows only + emulator
# os.path.join("/sdk", "cosmos", "azure-cosmos"),
os.path.join("/sdk", "ml", "azure-ai-ml"),
os.path.join("/sdk", "documentintelligence", "azure-ai-documentintelligence"),
os.path.join("/sdk", "ai", "azure-ai-inference"),
os.path.join("/sdk", "textanalytics", "azure-ai-textanalytics"),
os.path.join("/sdk", "translation", "azure-ai-translation-document"),
os.path.join("/sdk", "compute", "azure-mgmt-compute"),
os.path.join("/sdk", "communication", "azure-communication-chat"),
os.path.join("/sdk", "communication", "azure-communication-identity"),
os.path.join("/sdk", "storage", "azure-storage-blob")
# "/sdk/cosmos/azure-cosmos",
"/sdk/ml/azure-ai-ml",
"/sdk/documentintelligence/azure-ai-documentintelligence",
"/sdk/ai/azure-ai-inference",
"/sdk/textanalytics/azure-ai-textanalytics",
"/sdk/translation/azure-ai-translation-document",
"/sdk/compute/azure-mgmt-compute",
"/sdk/communication/azure-communication-chat",
"/sdk/communication/azure-communication-identity",
"/sdk/storage/azure-storage-blob"
],
"azure-mgmt-core": [
os.path.join("/sdk", "compute", "azure-mgmt-compute"),
os.path.join("/sdk", "network", "azure-mgmt-network"),
os.path.join("/sdk", "resources", "azure-mgmt-resource"),
os.path.join("/sdk", "keyvault", "azure-mgmt-keyvault")
"/sdk/compute/azure-mgmt-compute",
"/sdk/network/azure-mgmt-network",
"/sdk/resources/azure-mgmt-resource",
"/sdk/keyvault/azure-mgmt-keyvault"
]
}

2 changes: 1 addition & 1 deletion eng/tox/run_coverage.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
from ci_tools.functions import get_total_coverage

logging.basicConfig(level=logging.INFO)
coveragerc_file = os.path.join(os.path.dirname(__file__), "tox.ini")
coveragerc_file = os.path.join(os.path.dirname(__file__), "..", "..", ".coveragerc")

if __name__ == "__main__":
parser = argparse.ArgumentParser(
5 changes: 2 additions & 3 deletions scripts/devops_tasks/create_coverage.py
Original file line number Diff line number Diff line change
@@ -19,8 +19,7 @@

root_dir = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", ".."))
coverage_dir = os.path.join(root_dir, "_coverage/")
toxrc = os.path.join(root_dir, "eng","tox", "tox.ini")

coveragerc = os.path.join(root_dir, ".coveragerc")

def collect_tox_coverage_files():
coverage_version_cmd = [sys.executable, "-m", "coverage", "--version"]
@@ -53,7 +52,7 @@ def collect_tox_coverage_files():
def generate_coverage_xml():
if os.path.exists(coverage_dir):
logging.info("Generating coverage XML")
commands = ["coverage", "xml", "-i", "--rcfile", toxrc]
commands = ["coverage", "xml", "-i", "--rcfile", coveragerc]
run_check_call(commands, root_dir, always_exit=False)
else:
logging.error("Coverage file is not available in {} to generate coverage XML".format(coverage_dir))
2 changes: 1 addition & 1 deletion sdk/ai/azure-ai-inference/assets.json
Original file line number Diff line number Diff line change
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/ai/azure-ai-inference",
"Tag": "python/ai/azure-ai-inference_3f06cee8a7"
"Tag": "python/ai/azure-ai-inference_01cf9f82e3"
}
10 changes: 5 additions & 5 deletions sdk/ai/azure-ai-inference/azure/ai/inference/tracing.py
Original file line number Diff line number Diff line change
@@ -208,7 +208,7 @@ def _add_request_chat_message_events(self, span: "AbstractSpan", **kwargs: Any)
f"gen_ai.{message.get('role')}.message",
{
"gen_ai.system": _INFERENCE_GEN_AI_SYSTEM_NAME,
"gen_ai.event.content": json.dumps(message),
"gen_ai.event.content": json.dumps(message, ensure_ascii=False),
},
timestamp,
)
@@ -300,7 +300,7 @@ def _add_response_chat_message_events(
full_response["message"]["tool_calls"] = [tool.as_dict() for tool in choice.message.tool_calls]
attributes = {
"gen_ai.system": _INFERENCE_GEN_AI_SYSTEM_NAME,
"gen_ai.event.content": json.dumps(full_response),
"gen_ai.event.content": json.dumps(full_response, ensure_ascii=False),
}
else:
response: Dict[str, Any] = {
@@ -318,7 +318,7 @@ def _add_response_chat_message_events(

attributes = {
"gen_ai.system": _INFERENCE_GEN_AI_SYSTEM_NAME,
"gen_ai.event.content": json.dumps(response),
"gen_ai.event.content": json.dumps(response, ensure_ascii=False),
}
last_event_timestamp_ns = self._record_event(span, "gen_ai.choice", attributes, last_event_timestamp_ns)

@@ -478,7 +478,7 @@ def __iter__( # pyright: ignore [reportIncompatibleMethodOverride]
)
attributes = {
"gen_ai.system": _INFERENCE_GEN_AI_SYSTEM_NAME,
"gen_ai.event.content": json.dumps(accumulate),
"gen_ai.event.content": json.dumps(accumulate, ensure_ascii=False),
}
self._instrumentor._record_event(span, "gen_ai.choice", attributes, previous_event_timestamp)
span.finish()
@@ -532,7 +532,7 @@ def _trace_stream_content(self) -> None:
self._accumulate["message"]["tool_calls"] = list(tools_no_recording)
attributes = {
"gen_ai.system": _INFERENCE_GEN_AI_SYSTEM_NAME,
"gen_ai.event.content": json.dumps(self._accumulate),
"gen_ai.event.content": json.dumps(self._accumulate, ensure_ascii=False),
}
self._last_event_timestamp_ns = self._instrumentor._record_event( # pylint: disable=protected-access, line-too-long # pyright: ignore [reportFunctionMemberAccess]
span, "gen_ai.choice", attributes, self._last_event_timestamp_ns
65 changes: 57 additions & 8 deletions sdk/ai/azure-ai-inference/tests/test_client_tracing.py
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import json
import os
import azure.ai.inference as sdk
from azure.ai.inference.tracing import AIInferenceInstrumentor
@@ -322,6 +323,58 @@ def test_chat_completion_tracing_content_recording_enabled(self, **kwargs):
assert events_match == True
AIInferenceInstrumentor().uninstrument()

@ServicePreparerChatCompletions()
@recorded_by_proxy
def test_chat_completion_tracing_content_unicode(self, **kwargs):
# Make sure code is not instrumented due to a previous test exception
try:
AIInferenceInstrumentor().uninstrument()
except RuntimeError as e:
pass
self.modify_env_var(CONTENT_TRACING_ENV_VARIABLE, "True")
client = self._create_chat_client(**kwargs)
processor, exporter = self.setup_memory_trace_exporter()
AIInferenceInstrumentor().instrument()
response = client.complete(
messages=[
sdk.models.SystemMessage(content="You are a helpful assistant."),
sdk.models.UserMessage(content="将“hello world”翻译成中文和乌克兰语"),
],
)
processor.force_flush()
spans = exporter.get_spans_by_name_starts_with("chat")
assert len(spans) == 1
expected_events = [
{
"name": "gen_ai.system.message",
"attributes": {
"gen_ai.system": "az.ai.inference",
"gen_ai.event.content": '{"role": "system", "content": "You are a helpful assistant."}',
},
},
{
"name": "gen_ai.user.message",
"attributes": {
"gen_ai.system": "az.ai.inference",
"gen_ai.event.content": '{"role": "user", "content": "将“hello world”翻译成中文和乌克兰语"}',
},
},
{
"name": "gen_ai.choice",
"attributes": {
"gen_ai.system": "az.ai.inference",
"gen_ai.event.content": '{"message": {"content": "*"}, "finish_reason": "stop", "index": 0}',
},
},
]
events_match = GenAiTraceVerifier().check_span_events(spans[0], expected_events)
assert events_match == True

completion_event_content = json.loads(spans[0].events[2].attributes["gen_ai.event.content"])
assert False == completion_event_content["message"]["content"].isascii()
assert response.choices[0].message.content == completion_event_content["message"]["content"]
AIInferenceInstrumentor().uninstrument()

@ServicePreparerChatCompletions()
@recorded_by_proxy
def test_chat_completion_streaming_tracing_content_recording_disabled(self, **kwargs):
@@ -344,14 +397,12 @@ def test_chat_completion_streaming_tracing_content_recording_disabled(self, **kw
)
response_content = ""
for update in response:
if update.choices:
if update.choices and update.choices[0].delta.content:
response_content = response_content + update.choices[0].delta.content
client.close()

processor.force_flush()
spans = exporter.get_spans_by_name_starts_with("chat ")
if len(spans) == 0:
spans = exporter.get_spans_by_name("chat")
spans = exporter.get_spans_by_name_starts_with("chat")
assert len(spans) == 1
span = spans[0]
expected_attributes = [
@@ -403,7 +454,7 @@ def test_chat_completion_streaming_tracing_content_recording_enabled(self, **kwa
)
response_content = ""
for update in response:
if update.choices:
if update.choices and update.choices[0].delta.content:
response_content = response_content + update.choices[0].delta.content
client.close()

@@ -527,9 +578,7 @@ def get_weather(city: str) -> str:
# With the additional tools information on hand, get another response from the model
response = client.complete(messages=messages, tools=[weather_description])
processor.force_flush()
spans = exporter.get_spans_by_name_starts_with("chat ")
if len(spans) == 0:
spans = exporter.get_spans_by_name("chat")
spans = exporter.get_spans_by_name_starts_with("chat")
assert len(spans) == 2
expected_attributes = [
("gen_ai.operation.name", "chat"),
Loading