From de78804b515d0b396302836743a330f9a6af34c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 08:03:36 -0300 Subject: [PATCH 1/4] Update all non-major dependencies (#1088) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pyproject.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bb1cf1ff..ca560ea6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,22 +51,22 @@ get-hashes = 'codemodder.scripts.get_hashes:main' [project.optional-dependencies] semgrep = [ - "semgrep>=1.130,<1.131", + "semgrep>=1.131,<1.132", ] test = [ "azure-ai-inference>=1.0.0b1,<2.0", "coverage>=7.10,<7.11", "coverage-threshold~=0.4", "defusedxml==0.7.1", - "types-defusedxml==0.7.0.20250708", + "types-defusedxml==0.7.0.20250809", "flask-wtf==1.2.2", - "types-WTForms==3.2.1.20250602", + "types-WTForms==3.2.1.20250809", "Flask<4", "httpx~=0.27", "Jinja2~=3.1.2", "jsonschema~=4.25.0", "lxml>=6.0.0,<6.1.0", - "openai>=1.97,<1.98", + "openai>=1.99,<1.100", "mock==5.2.*", "pre-commit<5", "Pyjwt~=2.10.0", @@ -86,14 +86,14 @@ test = [ "fickling~=0.1.0,>=0.1.3", "graphql-server~=3.0.0b7", "unidiff>=0.7.5", - "semgrep>=1.130,<1.131", + "semgrep>=1.131,<1.132", ] complexity = [ "radon==6.0.*", "xenon==0.9.*", ] openai = [ - "openai>=1.97,<1.98", + "openai>=1.99,<1.100", ] azure = [ "azure-ai-inference>=1.0.0b1,<2.0", From 4143c19c3e0afcbe72d936fde40ce09c3c0738a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 08:58:45 -0300 Subject: [PATCH 2/4] Update actions/checkout action to v5 (#1089) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/autoformat-pixeebot-prs.yaml | 2 +- .github/workflows/codemod_pygoat.yml | 4 ++-- .github/workflows/deploy_to_pypi.yml | 2 +- .github/workflows/integration_test.yml | 2 +- .github/workflows/lint.yml | 4 ++-- .github/workflows/pre-commit-autoupdate.yml | 2 +- .github/workflows/test.yml | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/autoformat-pixeebot-prs.yaml b/.github/workflows/autoformat-pixeebot-prs.yaml index 9baa1cc0..0407acaf 100644 --- a/.github/workflows/autoformat-pixeebot-prs.yaml +++ b/.github/workflows/autoformat-pixeebot-prs.yaml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/codemod_pygoat.yml b/.github/workflows/codemod_pygoat.yml index 86e744d2..3d19d59e 100644 --- a/.github/workflows/codemod_pygoat.yml +++ b/.github/workflows/codemod_pygoat.yml @@ -22,7 +22,7 @@ jobs: timeout-minutes: 10 steps: - name: Check out codemodder - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python uses: actions/setup-python@v5 with: @@ -33,7 +33,7 @@ jobs: - name: Install Test Dependencies run: pip install ".[test]" - name: Check out Pygoat - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: pixee/pygoat path: pygoat diff --git a/.github/workflows/deploy_to_pypi.yml b/.github/workflows/deploy_to_pypi.yml index 8566a102..3dcc9c02 100644 --- a/.github/workflows/deploy_to_pypi.yml +++ b/.github/workflows/deploy_to_pypi.yml @@ -16,7 +16,7 @@ jobs: with: python-version: '3.13' - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install build dependencies run: pip install build twine - name: Build package diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index ebe170b0..b500468d 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -25,7 +25,7 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python uses: actions/setup-python@v5 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 98b41f19..32416788 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,7 +22,7 @@ jobs: timeout-minutes: 3 steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python uses: actions/setup-python@v5 with: @@ -41,6 +41,6 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index 30397213..35460f73 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -11,7 +11,7 @@ jobs: auto-update: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 - uses: browniebroke/pre-commit-autoupdate-action@main - uses: peter-evans/create-pull-request@v7 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d2bac223..e8cd6d23 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: timeout-minutes: 5 steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python uses: actions/setup-python@v5 with: @@ -45,7 +45,7 @@ jobs: timeout-minutes: 5 steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python uses: actions/setup-python@v5 with: @@ -65,7 +65,7 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python uses: actions/setup-python@v5 with: From aabccde8d2395b7901a97e5368142a10393887b9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 08:58:03 -0300 Subject: [PATCH 3/4] Update all non-major dependencies (#1090) * Update all non-major dependencies * fix graphql --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: clavedeluna --- ...est_sonar_disable_graphql_introspection.py | 2 +- .../test_disable_graphql_introspection.py | 4 ++-- pyproject.toml | 12 +++++------ .../disable_graphql_introspection.py | 2 ++ ...ee_python_disable-graphql-introspection.md | 2 +- ...est_sonar_disable_graphql_introspection.py | 4 ++-- .../test_disable_graphql_introspection.py | 20 +++++++++---------- .../samples/disable_graphql_introspection.py | 2 +- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/integration_tests/sonar/test_sonar_disable_graphql_introspection.py b/integration_tests/sonar/test_sonar_disable_graphql_introspection.py index f8128e17..2bc37a2f 100644 --- a/integration_tests/sonar/test_sonar_disable_graphql_introspection.py +++ b/integration_tests/sonar/test_sonar_disable_graphql_introspection.py @@ -11,7 +11,7 @@ class TestSonarDisableGraphQLIntrospection(SonarIntegrationTest): codemod = SonarDisableGraphQLIntrospection code_path = "tests/samples/disable_graphql_introspection.py" expected_new_code = """\ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from graphql import ( GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString) diff --git a/integration_tests/test_disable_graphql_introspection.py b/integration_tests/test_disable_graphql_introspection.py index 3d11d95c..947bb4d6 100644 --- a/integration_tests/test_disable_graphql_introspection.py +++ b/integration_tests/test_disable_graphql_introspection.py @@ -8,7 +8,7 @@ class TestDisableGraphQLIntrospection(BaseIntegrationTest): codemod = DisableGraphQLIntrospection original_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from graphql import ( GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString) @@ -32,7 +32,7 @@ class TestDisableGraphQLIntrospection(BaseIntegrationTest): ) """ expected_new_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from graphql import ( GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString) diff --git a/pyproject.toml b/pyproject.toml index ca560ea6..e41a3f85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,14 +51,14 @@ get-hashes = 'codemodder.scripts.get_hashes:main' [project.optional-dependencies] semgrep = [ - "semgrep>=1.131,<1.132", + "semgrep>=1.134,<1.135", ] test = [ "azure-ai-inference>=1.0.0b1,<2.0", "coverage>=7.10,<7.11", "coverage-threshold~=0.4", "defusedxml==0.7.1", - "types-defusedxml==0.7.0.20250809", + "types-defusedxml==0.7.0.20250822", "flask-wtf==1.2.2", "types-WTForms==3.2.1.20250809", "Flask<4", @@ -66,7 +66,7 @@ test = [ "Jinja2~=3.1.2", "jsonschema~=4.25.0", "lxml>=6.0.0,<6.1.0", - "openai>=1.99,<1.100", + "openai>=1.102,<1.103", "mock==5.2.*", "pre-commit<5", "Pyjwt~=2.10.0", @@ -84,16 +84,16 @@ test = [ "numpy ~= 2.3.0; python_version > '3.10'", "flask_wtf~=1.2.0", "fickling~=0.1.0,>=0.1.3", - "graphql-server~=3.0.0b7", + "graphql-server~=3.0.0b9", "unidiff>=0.7.5", - "semgrep>=1.131,<1.132", + "semgrep>=1.134,<1.135", ] complexity = [ "radon==6.0.*", "xenon==0.9.*", ] openai = [ - "openai>=1.99,<1.100", + "openai>=1.102,<1.103", ] azure = [ "azure-ai-inference>=1.0.0b1,<2.0", diff --git a/src/core_codemods/disable_graphql_introspection.py b/src/core_codemods/disable_graphql_introspection.py index 38d0428b..80d44ffa 100644 --- a/src/core_codemods/disable_graphql_introspection.py +++ b/src/core_codemods/disable_graphql_introspection.py @@ -41,6 +41,8 @@ class FindGraphQLViewsWithIntrospection( supported_functions = { "graphql_server.flask.GraphQLView", "graphql_server.flask.GraphQLView.as_view", + "graphql_server.flask.views.GraphQLView", + "graphql_server.flask.views.GraphQLView.as_view", "graphql_server.sanic.GraphQLView", "graphql_server.aiohttp.GraphQLView", "graphql_server.webob.GraphQLView", diff --git a/src/core_codemods/docs/pixee_python_disable-graphql-introspection.md b/src/core_codemods/docs/pixee_python_disable-graphql-introspection.md index eff757c9..532641a1 100644 --- a/src/core_codemods/docs/pixee_python_disable-graphql-introspection.md +++ b/src/core_codemods/docs/pixee_python_disable-graphql-introspection.md @@ -4,7 +4,7 @@ Introspection is often enabled by default in GraphQL without authentication. Thi Our changes look something like this: ```diff -from graphql_server.flask import GraphQLView +from graphql_server.flask.views import GraphQLView from flask import Flask from graphql import ( GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString) diff --git a/tests/codemods/sonar/test_sonar_disable_graphql_introspection.py b/tests/codemods/sonar/test_sonar_disable_graphql_introspection.py index d507f6a4..768030b6 100644 --- a/tests/codemods/sonar/test_sonar_disable_graphql_introspection.py +++ b/tests/codemods/sonar/test_sonar_disable_graphql_introspection.py @@ -15,7 +15,7 @@ def test_name(self): def test_simple(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema @@ -29,7 +29,7 @@ def test_simple(self, tmpdir): ) """ expected = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphql.validation import NoSchemaIntrospectionCustomRule diff --git a/tests/codemods/test_disable_graphql_introspection.py b/tests/codemods/test_disable_graphql_introspection.py index 60b35933..3cd488a0 100644 --- a/tests/codemods/test_disable_graphql_introspection.py +++ b/tests/codemods/test_disable_graphql_introspection.py @@ -12,7 +12,7 @@ def test_name(self): def test_simple_flask(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema @@ -26,7 +26,7 @@ def test_simple_flask(self, tmpdir): ) """ expected = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphql.validation import NoSchemaIntrospectionCustomRule @@ -77,7 +77,7 @@ def test_simple_constructor(self, tmpdir, module): def test_add_indirect(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema @@ -89,7 +89,7 @@ def test_add_indirect(self, tmpdir): ) """ expected = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphql.validation import NoSchemaIntrospectionCustomRule @@ -105,7 +105,7 @@ def test_add_indirect(self, tmpdir): def test_add_list_double_indirect(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema @@ -117,7 +117,7 @@ def test_add_list_double_indirect(self, tmpdir): ) """ expected = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphql.validation import NoSchemaIntrospectionCustomRule @@ -133,7 +133,7 @@ def test_add_list_double_indirect(self, tmpdir): def test_add_dict_indirect(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema @@ -146,7 +146,7 @@ def test_add_dict_indirect(self, tmpdir): ) """ expected = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphql.validation import NoSchemaIntrospectionCustomRule @@ -163,7 +163,7 @@ def test_add_dict_indirect(self, tmpdir): def test_has_validation_rule(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphql.validation import NoSchemaIntrospectionCustomRule @@ -178,7 +178,7 @@ def test_has_validation_rule(self, tmpdir): def test_has_graphene_validation_rule(self, tmpdir): input_code = """ - from graphql_server.flask import GraphQLView + from graphql_server.flask.views import GraphQLView from flask import Flask from .schemas import schema from graphene.validation import DisableIntrospection diff --git a/tests/samples/disable_graphql_introspection.py b/tests/samples/disable_graphql_introspection.py index 50076dca..c757336d 100644 --- a/tests/samples/disable_graphql_introspection.py +++ b/tests/samples/disable_graphql_introspection.py @@ -1,4 +1,4 @@ -from graphql_server.flask import GraphQLView +from graphql_server.flask.views import GraphQLView from flask import Flask from graphql import ( GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString) From c8a90b2a46dfc173b2d4eed9e1a3c8948911f5fc Mon Sep 17 00:00:00 2001 From: Dani Alcala <112832187+clavedeluna@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:44:18 -0300 Subject: [PATCH 4/4] remove LLM client configs (#1091) --- src/codemodder/codemodder.py | 33 +++--- src/codemodder/context.py | 11 -- src/codemodder/llm.py | 85 -------------- tests/test_context.py | 213 ----------------------------------- 4 files changed, 13 insertions(+), 329 deletions(-) diff --git a/src/codemodder/codemodder.py b/src/codemodder/codemodder.py index 9ae4d37e..d646e863 100644 --- a/src/codemodder/codemodder.py +++ b/src/codemodder/codemodder.py @@ -14,7 +14,7 @@ from codemodder.codetf import CodeTF from codemodder.context import CodemodExecutionContext from codemodder.dependency import Dependency -from codemodder.llm import MisconfiguredAIClient, TokenUsage, log_token_usage +from codemodder.llm import TokenUsage, log_token_usage from codemodder.logging import configure_logger, log_list, log_section, logger from codemodder.project_analysis.file_parsers.package_store import PackageStore from codemodder.project_analysis.python_repo_manager import PythonRepoManager @@ -134,7 +134,6 @@ def run( original_cli_args: list[str] | None = None, codemod_registry: registry.CodemodRegistry | None = None, sast_only: bool = False, - ai_client: bool = True, log_matched_files: bool = False, remediation: bool = False, ) -> tuple[CodeTF | None, int, TokenUsage]: @@ -162,24 +161,18 @@ def run( repo_manager = PythonRepoManager(Path(directory)) - try: - context = CodemodExecutionContext( - Path(directory), - dry_run, - verbose, - codemod_registry, - provider_registry, - repo_manager, - path_include, - path_exclude, - tool_result_files_map, - max_workers, - ai_client, - ) - except MisconfiguredAIClient as e: - logger.error(e) - # Codemodder instructions conflicted (according to spec) - return None, 3, token_usage + context = CodemodExecutionContext( + Path(directory), + dry_run, + verbose, + codemod_registry, + provider_registry, + repo_manager, + path_include, + path_exclude, + tool_result_files_map, + max_workers, + ) context.repo_manager.parse_project() diff --git a/src/codemodder/context.py b/src/codemodder/context.py index 4fe48541..5dda1db0 100644 --- a/src/codemodder/context.py +++ b/src/codemodder/context.py @@ -17,7 +17,6 @@ build_failed_dependency_notification, ) from codemodder.file_context import FileContext -from codemodder.llm import setup_azure_llama_llm_client, setup_openai_llm_client from codemodder.logging import log_list, logger from codemodder.project_analysis.file_parsers.package_store import PackageStore from codemodder.project_analysis.python_repo_manager import PythonRepoManager @@ -28,9 +27,6 @@ from codemodder.utils.update_finding_metadata import update_finding_metadata if TYPE_CHECKING: - from azure.ai.inference import ChatCompletionsClient - from openai import OpenAI - from codemodder.codemods.base_codemod import BaseCodemod @@ -51,8 +47,6 @@ class CodemodExecutionContext: max_workers: int = 1 tool_result_files_map: dict[str, list[Path]] semgrep_prefilter_results: ResultSet | None = None - openai_llm_client: OpenAI | None = None - azure_llama_llm_client: ChatCompletionsClient | None = None def __init__( self, @@ -66,7 +60,6 @@ def __init__( path_exclude: list[str] | None = None, tool_result_files_map: dict[str, list[Path]] | None = None, max_workers: int = 1, - ai_client: bool = True, ): self.directory = directory self.dry_run = dry_run @@ -85,10 +78,6 @@ def __init__( self.max_workers = max_workers self.tool_result_files_map = tool_result_files_map or {} self.semgrep_prefilter_results = None - self.openai_llm_client = setup_openai_llm_client() if ai_client else None - self.azure_llama_llm_client = ( - setup_azure_llama_llm_client() if ai_client else None - ) def add_changesets(self, codemod_name: str, change_sets: List[ChangeSet]): self._changesets_by_codemod.setdefault(codemod_name, []).extend(change_sets) diff --git a/src/codemodder/llm.py b/src/codemodder/llm.py index f9d66a28..2ec60590 100644 --- a/src/codemodder/llm.py +++ b/src/codemodder/llm.py @@ -2,35 +2,13 @@ import os from dataclasses import dataclass -from typing import TYPE_CHECKING from typing_extensions import Self -try: - from openai import AzureOpenAI, OpenAI -except ImportError: - OpenAI = None - AzureOpenAI = None - -try: - from azure.ai.inference import ChatCompletionsClient - from azure.core.credentials import AzureKeyCredential -except ImportError: - ChatCompletionsClient = None - AzureKeyCredential = None - -if TYPE_CHECKING: - from openai import OpenAI - from azure.ai.inference import ChatCompletionsClient - from azure.core.credentials import AzureKeyCredential - from codemodder.logging import logger __all__ = [ "MODELS", - "setup_openai_llm_client", - "setup_azure_llama_llm_client", - "MisconfiguredAIClient", "TokenUsage", "log_token_usage", ] @@ -42,7 +20,6 @@ "o1-mini", "o1", ] -DEFAULT_AZURE_OPENAI_API_VERSION = "2024-02-01" class ModelRegistry(dict): @@ -66,68 +43,6 @@ def __getattr__(self, name): MODELS = ModelRegistry(models) -def setup_openai_llm_client() -> OpenAI | None: - """Configure either the Azure OpenAI LLM client or the OpenAI client, in that order.""" - if not AzureOpenAI: - logger.info("Azure OpenAI API client not available") - return None - - azure_openapi_key = os.getenv("CODEMODDER_AZURE_OPENAI_API_KEY") - azure_openapi_endpoint = os.getenv("CODEMODDER_AZURE_OPENAI_ENDPOINT") - if bool(azure_openapi_key) ^ bool(azure_openapi_endpoint): - raise MisconfiguredAIClient( - "Azure OpenAI API key and endpoint must both be set or unset" - ) - - if azure_openapi_key and azure_openapi_endpoint: - logger.info("Using Azure OpenAI API client") - return AzureOpenAI( - api_key=azure_openapi_key, - api_version=os.getenv( - "CODEMODDER_AZURE_OPENAI_API_VERSION", - DEFAULT_AZURE_OPENAI_API_VERSION, - ), - azure_endpoint=azure_openapi_endpoint, - ) - - if not OpenAI: - logger.info("OpenAI API client not available") - return None - - if not (api_key := os.getenv("CODEMODDER_OPENAI_API_KEY")): - logger.info("OpenAI API key not found") - return None - - logger.info("Using OpenAI API client") - return OpenAI(api_key=api_key) - - -def setup_azure_llama_llm_client() -> ChatCompletionsClient | None: - """Configure the Azure Llama LLM client.""" - if not ChatCompletionsClient: - logger.info("Azure Llama client not available") - return None - - azure_llama_key = os.getenv("CODEMODDER_AZURE_LLAMA_API_KEY") - azure_llama_endpoint = os.getenv("CODEMODDER_AZURE_LLAMA_ENDPOINT") - if bool(azure_llama_key) ^ bool(azure_llama_endpoint): - raise MisconfiguredAIClient( - "Azure Llama API key and endpoint must both be set or unset" - ) - - if azure_llama_key and azure_llama_endpoint: - logger.info("Using Azure Llama API client") - return ChatCompletionsClient( - credential=AzureKeyCredential(azure_llama_key), - endpoint=azure_llama_endpoint, - ) - return None - - -class MisconfiguredAIClient(ValueError): - pass - - @dataclass class TokenUsage: completion_tokens: int = 0 diff --git a/tests/test_context.py b/tests/test_context.py index 8c7ef197..0c4fc85c 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -1,8 +1,4 @@ -import os - import pytest -from azure.ai.inference import ChatCompletionsClient -from openai import AzureOpenAI, OpenAI from codemodder.codetf import ( DetectionTool, @@ -22,7 +18,6 @@ ) from codemodder.context import CodemodExecutionContext as Context from codemodder.dependency import Security -from codemodder.llm import DEFAULT_AZURE_OPENAI_API_VERSION, MisconfiguredAIClient from codemodder.project_analysis.python_repo_manager import PythonRepoManager from codemodder.registry import load_registered_codemods @@ -107,214 +102,6 @@ def test_failed_dependency_description(self, mocker): in description ) - def test_setup_llm_clients_no_env_vars(self, mocker): - mocker.patch.dict(os.environ, clear=True) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - assert context.openai_llm_client is None - assert context.azure_llama_llm_client is None - - def test_setup_openai_llm_client(self, mocker): - mocker.patch.dict(os.environ, {"CODEMODDER_OPENAI_API_KEY": "test"}) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - assert isinstance(context.openai_llm_client, OpenAI) - - def test_setup_both_llm_clients(self, mocker): - mocker.patch.dict( - os.environ, - { - "CODEMODDER_OPENAI_API_KEY": "test", - "CODEMODDER_AZURE_LLAMA_API_KEY": "test", - "CODEMODDER_AZURE_LLAMA_ENDPOINT": "test", - }, - ) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - assert isinstance(context.openai_llm_client, OpenAI) - assert isinstance(context.azure_llama_llm_client, ChatCompletionsClient) - - def test_setup_azure_llm_client(self, mocker): - mocker.patch.dict( - os.environ, - { - "CODEMODDER_AZURE_OPENAI_API_KEY": "test", - "CODEMODDER_AZURE_OPENAI_ENDPOINT": "test", - }, - ) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - assert isinstance(context.openai_llm_client, AzureOpenAI) - assert ( - context.openai_llm_client._api_version == DEFAULT_AZURE_OPENAI_API_VERSION - ) - - @pytest.mark.parametrize( - "env_var", - ["CODEMODDER_AZURE_OPENAI_API_KEY", "CODEMODDER_AZURE_OPENAI_ENDPOINT"], - ) - def test_setup_azure_llm_client_missing_one(self, mocker, env_var): - mocker.patch.dict(os.environ, {env_var: "test"}) - with pytest.raises(MisconfiguredAIClient): - Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - - def test_setup_azure_llama_llm_client(self, mocker): - mocker.patch.dict( - os.environ, - { - "CODEMODDER_AZURE_LLAMA_API_KEY": "test", - "CODEMODDER_AZURE_LLAMA_ENDPOINT": "test", - }, - ) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - assert isinstance(context.azure_llama_llm_client, ChatCompletionsClient) - - @pytest.mark.parametrize( - "env_var", - ["CODEMODDER_AZURE_LLAMA_API_KEY", "CODEMODDER_AZURE_LLAMA_ENDPOINT"], - ) - def test_setup_azure_llama_llm_client_missing_one(self, mocker, env_var): - mocker.patch.dict(os.environ, {env_var: "test"}) - with pytest.raises(MisconfiguredAIClient): - Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - - def test_get_api_version_from_env(self, mocker): - version = "fake-version" - mocker.patch.dict( - os.environ, - { - "CODEMODDER_AZURE_OPENAI_API_KEY": "test", - "CODEMODDER_AZURE_OPENAI_ENDPOINT": "test", - "CODEMODDER_AZURE_OPENAI_API_VERSION": version, - }, - ) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ) - assert isinstance(context.openai_llm_client, AzureOpenAI) - assert context.openai_llm_client._api_version == version - - def test_disable_ai_client_openai(self, mocker): - mocker.patch.dict(os.environ, {"CODEMODDER_OPENAI_API_KEY": "test"}) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ai_client=False, - ) - assert context.openai_llm_client is None - - def test_disable_ai_client_azure(self, mocker): - mocker.patch.dict( - os.environ, - { - "CODEMODDER_AZURE_OPENAI_API_KEY": "test", - "CODEMODDER_AZURE_OPENAI_ENDPOINT": "test", - }, - ) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ai_client=False, - ) - assert context.openai_llm_client is None - - @pytest.mark.parametrize( - "env_var", - ["CODEMODDER_AZURE_OPENAI_API_KEY", "CODEMODDER_AZURE_OPENAI_ENDPOINT"], - ) - def test_no_misconfiguration_ai_client_disabled(self, mocker, env_var): - mocker.patch.dict(os.environ, {env_var: "test"}) - context = Context( - mocker.Mock(), - True, - False, - load_registered_codemods(), - None, - PythonRepoManager(mocker.Mock()), - [], - [], - ai_client=False, - ) - assert context.openai_llm_client is None - def test_compile_results(self, mocker): rule = rule = Rule( id="roslyn.sonaranalyzer.security.cs:S5131",