Skip to content

Commit

Permalink
Python: Adjust tool limit per request (#9894)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->
We have set incorrect limits on the number of tools that can be provided
to the AI services per request.

### Description

1. This PR removes the limits and will have requests fail on the service
side if there is any limit.
2. Add unit tests for prompt execution settings of some services that
are previously lacking such tests.
3. Fix the test coverage workflow.

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

---------

Co-authored-by: Evan Mattson <[email protected]>
  • Loading branch information
TaoChenOSU and moonbox3 authored Dec 10, 2024
1 parent ceae00c commit 31ec20e
Show file tree
Hide file tree
Showing 16 changed files with 644 additions and 84 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
Expand Up @@ -32,7 +32,6 @@ class AnthropicChatPromptExecutionSettings(AnthropicPromptExecutionSettings):
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description=(
"Do not set this manually. It is set by the service based on the function choice configuration."
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from semantic_kernel.connectors.ai.azure_ai_inference.azure_ai_inference_prompt_execution_settings import (
AzureAIInferenceChatPromptExecutionSettings,
AzureAIInferenceEmbeddingPromptExecutionSettings,
AzureAIInferencePromptExecutionSettings,
)
from semantic_kernel.connectors.ai.azure_ai_inference.azure_ai_inference_settings import AzureAIInferenceSettings
from semantic_kernel.connectors.ai.azure_ai_inference.services.azure_ai_inference_chat_completion import (
Expand All @@ -16,6 +17,7 @@
"AzureAIInferenceChatCompletion",
"AzureAIInferenceChatPromptExecutionSettings",
"AzureAIInferenceEmbeddingPromptExecutionSettings",
"AzureAIInferencePromptExecutionSettings",
"AzureAIInferenceSettings",
"AzureAIInferenceTextEmbedding",
]
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class AzureAIInferenceChatPromptExecutionSettings(AzureAIInferencePromptExecutio
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class BedrockChatPromptExecutionSettings(BedrockPromptExecutionSettings):
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class GoogleAIChatPromptExecutionSettings(GoogleAIPromptExecutionSettings):
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import override # pragma: no cover
else:
from typing_extensions import override # pragma: no cover

from pydantic import Field
from vertexai.generative_models import Tool, ToolConfig

Expand Down Expand Up @@ -38,7 +39,6 @@ class VertexAIChatPromptExecutionSettings(VertexAIPromptExecutionSettings):
tools: Annotated[
list[Tool] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ class MistralAIChatPromptExecutionSettings(MistralAIPromptExecutionSettings):
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ class OllamaPromptExecutionSettings(PromptExecutionSettings):
format: Literal["json"] | None = None
options: dict[str, Any] | None = None

# TODO(@taochen): Add individual properties for execution settings and
# convert them to the appropriate types in the options dictionary.


class OllamaTextPromptExecutionSettings(OllamaPromptExecutionSettings):
"""Settings for Ollama text prompt execution."""
Expand All @@ -32,7 +29,6 @@ class OllamaChatPromptExecutionSettings(OllamaPromptExecutionSettings):
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ class OpenAIChatPromptExecutionSettings(OpenAIPromptExecutionSettings):
tools: Annotated[
list[dict[str, Any]] | None,
Field(
max_length=64,
description="Do not set this manually. It is set by the service based "
"on the function choice configuration.",
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright (c) Microsoft. All rights reserved.

from semantic_kernel.connectors.ai.azure_ai_inference import (
AzureAIInferenceChatPromptExecutionSettings,
AzureAIInferenceEmbeddingPromptExecutionSettings,
AzureAIInferencePromptExecutionSettings,
)
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings


def test_default_azure_ai_inference_prompt_execution_settings():
settings = AzureAIInferencePromptExecutionSettings()

assert settings.frequency_penalty is None
assert settings.max_tokens is None
assert settings.presence_penalty is None
assert settings.seed is None
assert settings.stop is None
assert settings.temperature is None
assert settings.top_p is None
assert settings.extra_parameters is None


def test_custom_azure_ai_inference_prompt_execution_settings():
settings = AzureAIInferencePromptExecutionSettings(
frequency_penalty=0.5,
max_tokens=128,
presence_penalty=0.5,
seed=1,
stop="world",
temperature=0.5,
top_p=0.5,
extra_parameters={"key": "value"},
)

assert settings.frequency_penalty == 0.5
assert settings.max_tokens == 128
assert settings.presence_penalty == 0.5
assert settings.seed == 1
assert settings.stop == "world"
assert settings.temperature == 0.5
assert settings.top_p == 0.5
assert settings.extra_parameters == {"key": "value"}


def test_azure_ai_inference_prompt_execution_settings_from_default_completion_config():
settings = PromptExecutionSettings(service_id="test_service")
chat_settings = AzureAIInferenceChatPromptExecutionSettings.from_prompt_execution_settings(settings)

assert chat_settings.service_id == "test_service"
assert chat_settings.frequency_penalty is None
assert chat_settings.max_tokens is None
assert chat_settings.presence_penalty is None
assert chat_settings.seed is None
assert chat_settings.stop is None
assert chat_settings.temperature is None
assert chat_settings.top_p is None
assert chat_settings.extra_parameters is None


def test_azure_ai_inference_prompt_execution_settings_from_openai_prompt_execution_settings():
chat_settings = AzureAIInferenceChatPromptExecutionSettings(service_id="test_service", temperature=1.0)
new_settings = AzureAIInferencePromptExecutionSettings(service_id="test_2", temperature=0.0)
chat_settings.update_from_prompt_execution_settings(new_settings)

assert chat_settings.service_id == "test_2"
assert chat_settings.temperature == 0.0


def test_azure_ai_inference_prompt_execution_settings_from_custom_completion_config():
settings = PromptExecutionSettings(
service_id="test_service",
extension_data={
"frequency_penalty": 0.5,
"max_tokens": 128,
"presence_penalty": 0.5,
"seed": 1,
"stop": "world",
"temperature": 0.5,
"top_p": 0.5,
"extra_parameters": {"key": "value"},
},
)
chat_settings = AzureAIInferenceChatPromptExecutionSettings.from_prompt_execution_settings(settings)

assert chat_settings.service_id == "test_service"
assert chat_settings.frequency_penalty == 0.5
assert chat_settings.max_tokens == 128
assert chat_settings.presence_penalty == 0.5
assert chat_settings.seed == 1
assert chat_settings.stop == "world"
assert chat_settings.temperature == 0.5
assert chat_settings.top_p == 0.5
assert chat_settings.extra_parameters == {"key": "value"}


def test_azure_ai_inference_chat_prompt_execution_settings_from_custom_completion_config_with_functions():
settings = PromptExecutionSettings(
service_id="test_service",
extension_data={
"tools": [{"function": {}}],
},
)
chat_settings = AzureAIInferenceChatPromptExecutionSettings.from_prompt_execution_settings(settings)

assert chat_settings.tools == [{"function": {}}]


def test_create_options():
settings = AzureAIInferenceChatPromptExecutionSettings(
service_id="test_service",
extension_data={
"frequency_penalty": 0.5,
"max_tokens": 128,
"presence_penalty": 0.5,
"seed": 1,
"stop": "world",
"temperature": 0.5,
"top_p": 0.5,
"extra_parameters": {"key": "value"},
},
)
options = settings.prepare_settings_dict()

assert options["frequency_penalty"] == 0.5
assert options["max_tokens"] == 128
assert options["presence_penalty"] == 0.5
assert options["seed"] == 1
assert options["stop"] == "world"
assert options["temperature"] == 0.5
assert options["top_p"] == 0.5
assert options["extra_parameters"] == {"key": "value"}
assert "tools" not in options
assert "tool_config" not in options


def test_default_azure_ai_inference_embedding_prompt_execution_settings():
settings = AzureAIInferenceEmbeddingPromptExecutionSettings()

assert settings.dimensions is None
assert settings.encoding_format is None
assert settings.input_type is None
assert settings.extra_parameters is None
Loading

0 comments on commit 31ec20e

Please sign in to comment.