Skip to content

Commit

Permalink
Merge branch 'main' into issue-9902
Browse files Browse the repository at this point in the history
  • Loading branch information
malandis authored Dec 10, 2024
2 parents e199e24 + 43235b8 commit a026849
Show file tree
Hide file tree
Showing 67 changed files with 1,165 additions and 3,863 deletions.
62 changes: 30 additions & 32 deletions .github/workflows/python-test-coverage.yml
Original file line number Diff line number Diff line change
@@ -1,55 +1,53 @@
name: Python Test Coverage

on:
pull_request_target:
pull_request:
branches: ["main", "feature*"]
paths:
- "python/**"
workflow_run:
workflows: ["Python Unit Tests"]
types:
- in_progress
- "python/semantic_kernel/**"
- "python/tests/unit/**"
env:
# Configure a constant location for the uv cache
UV_CACHE_DIR: /tmp/.uv-cache

permissions:
contents: write
checks: write
pull-requests: write

jobs:
python-tests-coverage:
runs-on: ubuntu-latest
continue-on-error: true
permissions:
pull-requests: write
contents: read
actions: read
continue-on-error: false
defaults:
run:
working-directory: python
env:
UV_PYTHON: "3.10"
steps:
- name: Wait for unit tests to succeed
uses: lewagon/[email protected]
with:
ref: ${{ github.event.pull_request.head.sha }}
check-name: 'Python Test Coverage'
repo-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
wait-interval: 90
allowed-conclusions: success
- uses: actions/checkout@v4
- name: Setup filename variables
run: echo "FILE_ID=${{ github.event.number }}" >> $GITHUB_ENV
- name: Download Files
uses: actions/download-artifact@v4
- name: Set up uv
uses: astral-sh/setup-uv@v4
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
run-id: ${{ github.event.workflow_run.id }}
path: python/
merge-multiple: true
- name: Display structure of downloaded files
run: ls python/
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
- name: Install the project
run: uv sync --all-extras --dev
- name: Test with pytest
run: uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered ./tests/unit | tee python-coverage.txt
- name: Pytest coverage comment
id: coverageComment
uses: MishaKav/pytest-coverage-comment@main
continue-on-error: true
continue-on-error: false
with:
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
pytest-coverage-path: python-coverage.txt
pytest-coverage-path: python/python-coverage.txt
coverage-path-prefix: "python/"
title: "Python Test Coverage Report"
badge-title: "Python Test Coverage"
junitxml-title: "Python Unit Test Overview"
junitxml-path: pytest.xml
junitxml-path: python/pytest.xml
default-branch: "main"
unique-id-for-comment: python-test-coverage
report-only-changed-files: true
41 changes: 0 additions & 41 deletions .github/workflows/python-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,44 +62,3 @@ jobs:
display-options: fEX
fail-on-empty: true
title: Test results
python-test-coverage:
name: Python Test Coverage
runs-on: [ubuntu-latest]
continue-on-error: true
permissions:
contents: write
defaults:
run:
working-directory: python
env:
UV_PYTHON: "3.10"
steps:
- uses: actions/checkout@v4
- name: Setup filename variables
run: echo "FILE_ID=${{ github.event.number }}" >> $GITHUB_ENV
- name: Set up uv
uses: astral-sh/setup-uv@v4
with:
version: "0.5.x"
enable-cache: true
cache-suffix: ${{ runner.os }}-${{ env.UV_PYTHON }}
- name: Install the project
run: uv sync --all-extras --dev
- name: Test with pytest
run: uv run --frozen pytest -q --junitxml=pytest.xml --cov=semantic_kernel --cov-report=term-missing:skip-covered ./tests/unit | tee python-coverage.txt
- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: python-coverage-${{ env.FILE_ID }}.txt
path: python/python-coverage.txt
overwrite: true
retention-days: 1
- name: Upload pytest.xml
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-${{ env.FILE_ID }}.xml
path: python/pytest.xml
overwrite: true
retention-days: 1
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using JsonSchemaMapper;
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel;

namespace Step04;
Expand All @@ -14,55 +11,18 @@ internal static class JsonSchemaGenerator
/// <summary>
/// Wrapper for generating a JSON schema as string from a .NET type.
/// </summary>
public static string FromType<SchemaType>()
public static string FromType<TSchemaType>()
{
JsonSerializerOptions options = new(JsonSerializerOptions.Default)
{
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
};
JsonSchemaMapperConfiguration config = new()
AIJsonSchemaCreateOptions config = new()
{
TreatNullObliviousAsNonNullable = true,
TransformSchemaNode = (context, schema) =>
{
// NOTE: This can be replaced with `IncludeAdditionalProperties = false` when upgraded to System.Json.Text 9.0.0
if (context.TypeInfo.Type == typeof(SchemaType))
{
schema["additionalProperties"] = false;
}

// Determine if a type or property and extract the relevant attribute provider
ICustomAttributeProvider? attributeProvider = context.PropertyInfo is not null
? context.PropertyInfo.AttributeProvider
: context.TypeInfo.Type;

// Look up any description attributes
DescriptionAttribute? descriptionAttr = attributeProvider?
.GetCustomAttributes(inherit: true)
.Select(attr => attr as DescriptionAttribute)
.FirstOrDefault(attr => attr is not null);

// Apply description attribute to the generated schema
if (descriptionAttr != null)
{
if (schema is not JsonObject jObj)
{
// Handle the case where the schema is a boolean
JsonValueKind valueKind = schema.GetValueKind();
schema = jObj = new JsonObject();
if (valueKind is JsonValueKind.False)
{
jObj.Add("not", true);
}
}

jObj["description"] = descriptionAttr.Description;
}

return schema;
}
IncludeSchemaKeyword = false,
DisallowAdditionalProperties = true,
};

return KernelJsonSchemaBuilder.Build(typeof(SchemaType), "Intent Result", config).AsJson();
return KernelJsonSchemaBuilder.Build(typeof(TSchemaType), "Intent Result", config).AsJson();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ public void ItCanConvertToFunctionDefinitionsWithParameterTypesAndReturnParamete
{ "type": "object",
"required": ["param1", "param2"],
"properties": {
"param1": { "type": "string", "description": "String param 1" },
"param2": { "type": "integer", "description": "Int param 2" } } }
"param1": { "description": "String param 1", "type": "string" },
"param2": { "description": "Int param 2" , "type": "integer"} } }
""";

KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("Tests", new[]
Expand Down Expand Up @@ -126,8 +126,8 @@ public void ItCanConvertToFunctionDefinitionsWithParameterTypesAndNoReturnParame
{ "type": "object",
"required": ["param1", "param2"],
"properties": {
"param1": { "type": "string", "description": "String param 1" },
"param2": { "type": "integer", "description": "Int param 2" } } }
"param1": { "description": "String param 1", "type": "string" },
"param2": { "description": "Int param 2", "type": "integer"} } }
""";

KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("Tests", new[]
Expand Down Expand Up @@ -180,7 +180,7 @@ public void ItCanConvertToFunctionDefinitionsWithNoParameterTypesButWithDescript

// Assert
Assert.Equal(
"""{"type":"object","required":[],"properties":{"param1":{"type":"string","description":"something neat"}}}""",
"""{"type":"object","required":[],"properties":{"param1":{"description":"something neat","type":"string"}}}""",
JsonSerializer.Serialize(result.Parameters));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public void ItCanCreateValidGeminiFunctionManualForPlugin()
// Assert
Assert.NotNull(result);
Assert.Equal(
"""{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"type":"string","description":"String parameter"},"parameter2":{"type":"string","enum":["Value1","Value2"],"description":"Enum parameter"},"parameter3":{"type":"string","format":"date-time","description":"DateTime parameter"}}}""",
"""{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"description":"String parameter","type":"string"},"parameter2":{"description":"Enum parameter","type":"string","enum":["Value1","Value2"]},"parameter3":{"description":"DateTime parameter","type":"string"}}}""",
JsonSerializer.Serialize(result.Parameters)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void ItCanConvertToFunctionDefinitionWithPluginName()
[Fact]
public void ItCanConvertToFunctionDefinitionsWithParameterTypesAndReturnParameterType()
{
string expectedParameterSchema = """{ "type": "object", "required": ["param1", "param2"], "properties": { "param1": { "type": "string", "description": "String param 1" }, "param2": { "type": "integer", "description": "Int param 2" } } } """;
string expectedParameterSchema = """{ "type": "object", "required": ["param1", "param2"], "properties": { "param1": { "description": "String param 1", "type": "string" }, "param2": { "description": "Int param 2", "type": "integer" } } } """;

KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("Tests", new[]
{
Expand All @@ -118,7 +118,7 @@ public void ItCanConvertToFunctionDefinitionsWithParameterTypesAndReturnParamete
[Fact]
public void ItCanConvertToFunctionDefinitionsWithParameterTypesAndNoReturnParameterType()
{
string expectedParameterSchema = """{ "type": "object", "required": ["param1", "param2"], "properties": { "param1": { "type": "string", "description": "String param 1" }, "param2": { "type": "integer", "description": "Int param 2" } } } """;
string expectedParameterSchema = """{ "type": "object", "required": ["param1", "param2"], "properties": { "param1": { "description": "String param 1", "type": "string" }, "param2": { "description": "Int param 2", "type": "integer" } } } """;

KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("Tests", new[]
{
Expand Down Expand Up @@ -174,7 +174,7 @@ public void ItCanConvertToFunctionDefinitionsWithNoParameterTypesButWithDescript
Assert.NotNull(pd.properties);
Assert.Single(pd.properties);
Assert.Equal(
JsonSerializer.Serialize(KernelJsonSchema.Parse("""{ "type":"string", "description":"something neat" }""")),
JsonSerializer.Serialize(KernelJsonSchema.Parse("""{ "description":"something neat", "type":"string" }""")),
JsonSerializer.Serialize(pd.properties.First().Value.RootElement));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.Json;
using JsonSchemaMapper;
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Xunit;

namespace SemanticKernel.Connectors.OpenAI.UnitTests.Core;

/// <summary>
/// Unit tests for <see cref="OpenAIJsonSchemaTransformer"/> class.
/// Unit tests for schema transformations used by OpenAI clients.
/// </summary>
public sealed class OpenAIJsonSchemaTransformerTests
{
private static readonly JsonSchemaMapperConfiguration s_jsonSchemaMapperConfiguration = new()
private static readonly AIJsonSchemaCreateOptions s_jsonSchemaCreateOptions = new()
{
IncludeSchemaVersion = false,
IncludeTypeInEnums = true,
TreatNullObliviousAsNonNullable = true,
TransformSchemaNode = OpenAIJsonSchemaTransformer.Transform,
IncludeSchemaKeyword = false,
IncludeTypeInEnumSchemas = true,
DisallowAdditionalProperties = true,
RequireAllProperties = true,
};

private static readonly JsonSerializerOptions s_jsonSerializerOptions = new()
Expand Down Expand Up @@ -124,7 +123,7 @@ public void ItTransformsJsonSchemaCorrectly()
""";

// Act
var schema = KernelJsonSchemaBuilder.Build(type, configuration: s_jsonSchemaMapperConfiguration);
var schema = KernelJsonSchemaBuilder.Build(type, configuration: s_jsonSchemaCreateOptions);

// Assert
Assert.Equal(NormalizeJson(expectedSchema), NormalizeJson(schema.ToString()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public void ItCanCreateValidAzureOpenAIFunctionManualForPlugin()
// Assert
Assert.NotNull(result);
Assert.Equal(
"""{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"type":"string","description":"String parameter"},"parameter2":{"type":"string","enum":["Value1","Value2"],"description":"Enum parameter"},"parameter3":{"type":"string","format":"date-time","description":"DateTime parameter"}}}""",
"""{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"description":"String parameter","type":"string"},"parameter2":{"description":"Enum parameter","type":"string","enum":["Value1","Value2"]},"parameter3":{"description":"DateTime parameter","type":"string"}}}""",
result.FunctionParameters.ToString()
);
}
Expand Down

This file was deleted.

Loading

0 comments on commit a026849

Please sign in to comment.