Skip to content

Conversation

Copy link

Copilot AI commented Jan 25, 2026

Users attempting to use get_bearer_token_provider(DefaultAzureCredential(), scope) with OpenAI targets encounter TypeError: object str can't be used in 'await' expression. The OpenAI SDK's AsyncOpenAI requires async token providers, but Azure's get_bearer_token_provider returns synchronous callables.

# This fails with cryptic error
from azure.identity import DefaultAzureCredential, get_bearer_token_provider

api_key = get_bearer_token_provider(
    DefaultAzureCredential(), 
    "https://cognitiveservices.azure.com/.default"
)
target = OpenAIChatTarget(endpoint=endpoint, api_key=api_key)  # TypeError at runtime

Changes

  • Added _ensure_async_token_provider() in openai_target.py

    • Detects sync callables via asyncio.iscoroutinefunction()
    • Wraps synchronous token providers in async functions
    • Preserves async callables and string API keys unchanged
    • Logs info message when wrapping occurs
  • Integrated into OpenAITarget.__init__()

    • Applied to api_key parameter after environment/parameter resolution
    • Transparent to callers
  • Updated type hints and docstrings

    • api_key: Optional[str | Callable[[], str | Awaitable[str]]]
    • Clarified both sync and async token providers are supported
  • Added test coverage

    • Unit tests for wrapping logic and all token provider types
    • Integration test validating sync provider with live Azure endpoints
    • Updated existing test expecting synchronous behavior

Result

Both sync and async token providers now work transparently:

# Sync provider (auto-wrapped)
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
api_key = get_bearer_token_provider(DefaultAzureCredential(), scope)

# Async provider (unchanged)
from pyrit.auth import get_azure_openai_auth
api_key = get_azure_openai_auth(endpoint)

# Both work with OpenAI targets
target = OpenAIChatTarget(endpoint=endpoint, api_key=api_key)
Original prompt

This section details on the original issue you should resolve

<issue_title>BUG Entra auth with synchronous token providers</issue_title>
<issue_description>#### Is your feature request related to a problem? Please describe.
Since we now support auth token providers via the api_key argument just like the openai SDK it is somewhat unintuitive that synchronous ones won't work. There's a cryptic error being shown if you try that.

E.g., people may try the following with any openai target:

api_key=get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default")

which for me yields

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File /workspace/pyrit/prompt_normalizer/prompt_normalizer.py:95, in PromptNormalizer.send_prompt_async(self, seed_group, target, conversation_id, request_converter_configurations, response_converter_configurations, labels, attack_identifier)
     94 try:
---> 95     responses = await target.send_prompt_async(message=request)
     96     self._memory.add_message_to_memory(request=request)

File /workspace/pyrit/prompt_target/common/utils.py:56, in limit_requests_per_minute.<locals>.set_max_rpm(*args, **kwargs)
     54     await asyncio.sleep(60 / rpm)
---> 56 return await func(*args, **kwargs)

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/tenacity/asyncio/__init__.py:189, in AsyncRetrying.wraps.<locals>.async_wrapped(*args, **kwargs)
    188 async_wrapped.statistics = copy.statistics  # type: ignore[attr-defined]
--> 189 return await copy(fn, *args, **kwargs)

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/tenacity/asyncio/__init__.py:111, in AsyncRetrying.__call__(self, fn, *args, **kwargs)
    110 while True:
--> 111     do = await self.iter(retry_state=retry_state)
    112     if isinstance(do, DoAttempt):

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/tenacity/asyncio/__init__.py:153, in AsyncRetrying.iter(self, retry_state)
    152 for action in self.iter_state.actions:
--> 153     result = await action(retry_state)
    154 return result

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/tenacity/_utils.py:99, in wrap_to_async_func.<locals>.inner(*args, **kwargs)
     98 async def inner(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
---> 99     return call(*args, **kwargs)

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/tenacity/__init__.py:400, in BaseRetrying._post_retry_check_actions.<locals>.<lambda>(rs)
    399 if not (self.iter_state.is_explicit_retry or self.iter_state.retry_run_result):
--> 400     self._add_action_func(lambda rs: rs.outcome.result())
    401     return

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/concurrent/futures/_base.py:449, in Future.result(self, timeout)
    448 elif self._state == FINISHED:
--> 449     return self.__get_result()
    451 self._condition.wait(timeout)

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/concurrent/futures/_base.py:401, in Future.__get_result(self)
    400 try:
--> 401     raise self._exception
    402 finally:
    403     # Break a reference cycle with the exception in self._exception

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/tenacity/asyncio/__init__.py:114, in AsyncRetrying.__call__(self, fn, *args, **kwargs)
    113 try:
--> 114     result = await fn(*args, **kwargs)
    115 except BaseException:  # noqa: B902

File /workspace/pyrit/prompt_target/openai/openai_chat_target.py:197, in OpenAIChatTarget.send_prompt_async(self, message)
    196 # Use unified error handling - automatically detects ChatCompletion and validates
--> 197 response = await self._handle_openai_request(
    198     api_call=lambda: self._async_client.chat.completions.create(**body),
    199     request=message,
    200 )
    201 return [response]

File /workspace/pyrit/prompt_target/openai/openai_target.py:372, in OpenAITarget._handle_openai_request(self, api_call, request)
    370 try:
    371     # Execute the API call
--> 372     response = await api_call()
    374     # Extract MessagePiece for validation and construction (most targets use single piece)

File ~/.conda/envs/pyrit-release-test1/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py:2678, in AsyncCompletions.create(self, messages, model, audio, frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, metadata, modalities, n, parallel_tool_calls, prediction, presence_penalty, prompt_cache_key, prompt_cache_retention, reasoning_effort, response_format, safety_identifier, seed, service_tier, stop, store, stream, stream_options, temperature, tool_choice, tools, top_logprobs, top_p, user, verbosity, web_search_options, extra_headers, extra_query, extra_body, timeout)
   2677 validate_response_format(respo...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes Azure/PyRIT#1252

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 2 commits January 25, 2026 13:38
Co-authored-by: romanlutz <10245648+romanlutz@users.noreply.github.com>
Co-authored-by: romanlutz <10245648+romanlutz@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix bug with synchronous token providers for Entra auth Auto-wrap synchronous token providers for AsyncOpenAI compatibility Jan 25, 2026
Copilot AI requested a review from romanlutz January 25, 2026 13:42
@romanlutz romanlutz linked an issue Jan 25, 2026 that may be closed by this pull request
@romanlutz
Copy link
Contributor

need to validate with integration test locally

@romanlutz romanlutz changed the title Auto-wrap synchronous token providers for AsyncOpenAI compatibility FIX Auto-wrap synchronous token providers for AsyncOpenAI compatibility Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG Entra auth with synchronous token providers

2 participants