From 31f480aac0925aa28af6c38d65ebeda4f4347ab0 Mon Sep 17 00:00:00 2001 From: "Alex (CrewAI)" Date: Wed, 15 Apr 2026 06:17:15 -0700 Subject: [PATCH] docs: update changelog and version for v1.14.2a4 --- docs/ar/changelog.mdx | 131 --------- docs/en/changelog.mdx | 131 --------- docs/ko/changelog.mdx | 131 --------- docs/pt-BR/changelog.mdx | 131 --------- lib/crewai/pyproject.toml | 1 + .../crewai/llms/providers/azure/completion.py | 82 +++++- .../llms/azure/test_azure_credentials.py | 257 ++++++++++++++++++ 7 files changed, 334 insertions(+), 530 deletions(-) create mode 100644 lib/crewai/tests/llms/azure/test_azure_credentials.py diff --git a/docs/ar/changelog.mdx b/docs/ar/changelog.mdx index eb714117d..b151cf950 100644 --- a/docs/ar/changelog.mdx +++ b/docs/ar/changelog.mdx @@ -4,137 +4,6 @@ description: "تحديثات المنتج والتحسينات وإصلاحات icon: "clock" mode: "wide" --- - - ## v1.14.3a2 - - [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) - - ## ما الذي تغير - - ### الميزات - - إضافة دعم لـ bedrock V4 - - إضافة أدوات Daytona sandbox لوظائف محسّنة - - إضافة صفحة "البناء باستخدام الذكاء الاصطناعي" — مستندات أصلية للذكاء الاصطناعي لوكلاء البرمجة - - إضافة "البناء باستخدام الذكاء الاصطناعي" إلى التنقل في صفحة "البدء" وملفات الصفحات لجميع اللغات (en, ko, pt-BR, ar) - - ### إصلاحات الأخطاء - - إصلاح انتشار أسماء @CrewBase الضمنية إلى أحداث الطاقم - - حل مشكلة تكرار تهيئة الدفعات في دمج بيانات التنفيذ الوصفية - - إصلاح تسلسل حقول مرجع فئة Task لعمليات التحقق من النقاط - - التعامل مع نتيجة BaseModel في حلقة إعادة المحاولة للحدود - - تحديث python-dotenv إلى الإصدار >=1.2.2 للامتثال الأمني - - ### الوثائق - - تحديث سجل التغييرات والإصدار لـ v1.14.3a1 - - تحديث الأوصاف وتطبيق الترجمات الفعلية - - ## المساهمون - - @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta - - - - - ## v1.14.3a1 - - [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) - - ## ما الذي تغير - - ### الميزات - - إضافة دعم نقاط التحقق والفروع لوكلاء مستقلين - - ### إصلاحات الأخطاء - - الحفاظ على thought_signature في استدعاءات أداة البث Gemini - - إصدار task_started عند استئناف الفرع وإعادة تصميم واجهة المستخدم النصية لنقاط التحقق - - تصحيح ترتيب التشغيل الجاف ومعالجة الفرع القديم الذي تم التحقق منه في إصدار أدوات التطوير - - استخدام تواريخ مستقبلية في اختبارات تقليم نقاط التحقق لمنع الفشل المعتمد على الوقت (#5543) - - ### الوثائق - - تحديث سجل التغييرات والإصدار لـ v1.14.2 - - ## المساهمون - - @alex-clawd, @greysonlalonde - - - - - ## v1.14.2 - - [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) - - ## ما الذي تغير - - ### الميزات - - إضافة أوامر استئناف النقاط التفتيش، والاختلاف، والتنظيف مع تحسين إمكانية الاكتشاف. - - إضافة معلمة `from_checkpoint` إلى `Agent.kickoff` والطرق ذات الصلة. - - إضافة أوامر إدارة القوالب لقوالب المشاريع. - - إضافة تلميحات استئناف إلى إصدار أدوات المطور عند الفشل. - - إضافة واجهة سطر الأوامر للتحقق من النشر وتعزيز سهولة استخدام تهيئة LLM. - - إضافة تقسيم النقاط التفتيشية مع تتبع النسب. - - إثراء تتبع رموز LLM مع رموز الاستدلال ورموز إنشاء التخزين المؤقت. - - ### إصلاحات الأخطاء - - إصلاح المطالبة بشأن تعارضات الفروع القديمة في إصدار أدوات المطور. - - تصحيح الثغرات في `authlib` و `langchain-text-splitters` و `pypdf`. - - تحديد نطاق معالجات البث لمنع تلوث أجزاء التشغيل المتقاطعة. - - إرسال نقاط التفتيش عبر واجهات Flow في TUI. - - استخدام نمط البحث المتكرر لاكتشاف نقاط التفتيش بتنسيق JSON. - - التعامل مع مخططات JSON الدائرية في أداة حل MCP. - - الحفاظ على معلمات استدعاء أداة Bedrock من خلال إزالة القيمة الافتراضية الصحيحة. - - إصدار حدث flow_finished بعد استئناف HITL. - - إصلاح ثغرات متنوعة من خلال تحديث التبعيات، بما في ذلك `requests` و `cryptography` و `pytest`. - - إصلاح لإيقاف تمرير وضع صارم إلى واجهة برمجة التطبيقات Bedrock Converse. - - ### الوثائق - - توثيق المعلمات المفقودة وإضافة قسم النقاط التفتيشية. - - تحديث سجل التغييرات والإصدار للإصدار v1.14.2 ومرشحي الإصدار السابقين. - - إضافة توثيق ميزة A2A الخاصة بالشركات وتحديث وثائق A2A المفتوحة المصدر. - - ## المساهمون - - @Yanhu007، @alex-clawd، @github-actions[bot]، @greysonlalonde، @iris-clawd، @lorenzejay، @lucasgomide - - - - - ## v1.14.2rc1 - - [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2rc1) - - ## ما الذي تغير - - ### إصلاحات الأخطاء - - إصلاح معالجة مخططات JSON الدائرية في أداة MCP - - إصلاح ثغرة أمنية من خلال تحديث python-multipart إلى 0.0.26 - - إصلاح ثغرة أمنية من خلال تحديث pypdf إلى 6.10.1 - - ### الوثائق - - تحديث سجل التغييرات والإصدار لـ v1.14.2a5 - - ## المساهمون - - @greysonlalonde - - - - - ## v1.14.2a5 - - [عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2a5) - - ## ما الذي تغير - - ### الوثائق - - تحديث سجل التغييرات والإصدار لـ v1.14.2a4 - - ## المساهمون - - @greysonlalonde - - - ## v1.14.2a4 diff --git a/docs/en/changelog.mdx b/docs/en/changelog.mdx index 5fdd624ff..24c62b85f 100644 --- a/docs/en/changelog.mdx +++ b/docs/en/changelog.mdx @@ -4,137 +4,6 @@ description: "Product updates, improvements, and bug fixes for CrewAI" icon: "clock" mode: "wide" --- - - ## v1.14.3a2 - - [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) - - ## What's Changed - - ### Features - - Add support for bedrock V4 - - Add Daytona sandbox tools for enhanced functionality - - Add 'Build with AI' page — AI-native docs for coding agents - - Add Build with AI to Get Started navigation and page files for all languages (en, ko, pt-BR, ar) - - ### Bug Fixes - - Fix propagation of implicit @CrewBase names to crew events - - Resolve issue with duplicate batch initialization in execution metadata merge - - Fix serialization of Task class-reference fields for checkpointing - - Handle BaseModel result in guardrail retry loop - - Bump python-dotenv to version >=1.2.2 for security compliance - - ### Documentation - - Update changelog and version for v1.14.3a1 - - Update descriptions and apply actual translations - - ## Contributors - - @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta - - - - - ## v1.14.3a1 - - [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) - - ## What's Changed - - ### Features - - Add checkpoint and fork support to standalone agents - - ### Bug Fixes - - Preserve thought_signature in Gemini streaming tool calls - - Emit task_started on fork resume and redesign checkpoint TUI - - Correct dry-run order and handle checked-out stale branch in devtools release - - Use future dates in checkpoint prune tests to prevent time-dependent failures (#5543) - - ### Documentation - - Update changelog and version for v1.14.2 - - ## Contributors - - @alex-clawd, @greysonlalonde - - - - - ## v1.14.2 - - [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) - - ## What's Changed - - ### Features - - Add checkpoint resume, diff, and prune commands with improved discoverability. - - Add `from_checkpoint` parameter to `Agent.kickoff` and related methods. - - Add template management commands for project templates. - - Add resume hints to devtools release on failure. - - Add deploy validation CLI and enhance LLM initialization ergonomics. - - Add checkpoint forking with lineage tracking. - - Enrich LLM token tracking with reasoning tokens and cache creation tokens. - - ### Bug Fixes - - Fix prompt on stale branch conflicts in devtools release. - - Patch vulnerabilities in `authlib`, `langchain-text-splitters`, and `pypdf`. - - Scope streaming handlers to prevent cross-run chunk contamination. - - Dispatch Flow checkpoints through Flow APIs in TUI. - - Use recursive glob for JSON checkpoint discovery. - - Handle cyclic JSON schemas in MCP tool resolution. - - Preserve Bedrock tool call arguments by removing truthy default. - - Emit flow_finished event after HITL resume. - - Fix various vulnerabilities by updating dependencies, including `requests`, `cryptography`, and `pytest`. - - Fix to stop forwarding strict mode to Bedrock Converse API. - - ### Documentation - - Document missing parameters and add Checkpointing section. - - Update changelog and version for v1.14.2 and previous release candidates. - - Add enterprise A2A feature documentation and update OSS A2A docs. - - ## Contributors - - @Yanhu007, @alex-clawd, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide - - - - - ## v1.14.2rc1 - - [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2rc1) - - ## What's Changed - - ### Bug Fixes - - Fix handling of cyclic JSON schemas in MCP tool resolution - - Fix vulnerability by bumping python-multipart to 0.0.26 - - Fix vulnerability by bumping pypdf to 6.10.1 - - ### Documentation - - Update changelog and version for v1.14.2a5 - - ## Contributors - - @greysonlalonde - - - - - ## v1.14.2a5 - - [View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2a5) - - ## What's Changed - - ### Documentation - - Update changelog and version for v1.14.2a4 - - ## Contributors - - @greysonlalonde - - - ## v1.14.2a4 diff --git a/docs/ko/changelog.mdx b/docs/ko/changelog.mdx index f744341eb..79c260bb4 100644 --- a/docs/ko/changelog.mdx +++ b/docs/ko/changelog.mdx @@ -4,137 +4,6 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정" icon: "clock" mode: "wide" --- - - ## v1.14.3a2 - - [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) - - ## 변경 사항 - - ### 기능 - - 베드록 V4 지원 추가 - - 향상된 기능을 위한 데이토나 샌드박스 도구 추가 - - 'AI와 함께 빌드' 페이지 추가 — 코딩 에이전트를 위한 AI 네이티브 문서 - - 모든 언어(en, ko, pt-BR, ar)에 대한 시작하기 탐색 및 페이지 파일에 AI와 함께 빌드 추가 - - ### 버그 수정 - - 크루 이벤트에 대한 암묵적 @CrewBase 이름 전파 수정 - - 실행 메타데이터 병합에서 중복 배치 초기화 문제 해결 - - 체크포인트를 위한 Task 클래스 참조 필드 직렬화 수정 - - 가드레일 재시도 루프에서 BaseModel 결과 처리 - - 보안 준수를 위해 python-dotenv를 버전 >=1.2.2로 업데이트 - - ### 문서 - - v1.14.3a1에 대한 변경 로그 및 버전 업데이트 - - 설명 업데이트 및 실제 번역 적용 - - ## 기여자 - - @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta - - - - - ## v1.14.3a1 - - [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) - - ## 변경 사항 - - ### 기능 - - 독립형 에이전트에 체크포인트 및 포크 지원 추가 - - ### 버그 수정 - - Gemini 스트리밍 도구 호출에서 thought_signature 보존 - - 포크 재개 시 task_started 방출 및 체크포인트 TUI 재설계 - - dry-run 순서 수정 및 devtools 릴리스에서 체크아웃된 오래된 브랜치 처리 - - 체크포인트 가지치기 테스트에서 미래 날짜 사용하여 시간 의존성 실패 방지 (#5543) - - ### 문서 - - v1.14.2에 대한 변경 로그 및 버전 업데이트 - - ## 기여자 - - @alex-clawd, @greysonlalonde - - - - - ## v1.14.2 - - [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) - - ## 변경 사항 - - ### 기능 - - 체크포인트 재개, 차이(diff), 및 가지치기(prune) 명령을 추가하여 가시성을 개선했습니다. - - `Agent.kickoff` 및 관련 메서드에 `from_checkpoint` 매개변수를 추가했습니다. - - 프로젝트 템플릿을 위한 템플릿 관리 명령을 추가했습니다. - - 실패 시 개발 도구 릴리스에 재개 힌트를 추가했습니다. - - 배포 검증 CLI를 추가하고 LLM 초기화의 사용 편의성을 향상시켰습니다. - - 계보 추적이 가능한 체크포인트 포킹을 추가했습니다. - - 추론 토큰 및 캐시 생성 토큰으로 LLM 토큰 추적을 풍부하게 했습니다. - - ### 버그 수정 - - 개발 도구 릴리스에서 오래된 브랜치 충돌에 대한 프롬프트를 수정했습니다. - - `authlib`, `langchain-text-splitters`, 및 `pypdf`의 취약점을 패치했습니다. - - 스트리밍 핸들러의 범위를 설정하여 교차 실행 청크 오염을 방지했습니다. - - TUI에서 Flow API를 통해 Flow 체크포인트를 전송했습니다. - - JSON 체크포인트 발견을 위해 재귀적 글로브를 사용했습니다. - - MCP 도구 해상도에서 순환 JSON 스키마를 처리했습니다. - - 진리값이 있는 기본값을 제거하여 Bedrock 도구 호출 인수를 보존했습니다. - - HITL 재개 후 flow_finished 이벤트를 발생시켰습니다. - - `requests`, `cryptography`, 및 `pytest`를 포함한 종속성을 업데이트하여 다양한 취약점을 수정했습니다. - - Bedrock Converse API에 엄격 모드를 전달하지 않도록 수정했습니다. - - ### 문서 - - 누락된 매개변수를 문서화하고 체크포인팅 섹션을 추가했습니다. - - v1.14.2 및 이전 릴리스 후보에 대한 변경 로그 및 버전을 업데이트했습니다. - - 기업 A2A 기능 문서를 추가하고 OSS A2A 문서를 업데이트했습니다. - - ## 기여자 - - @Yanhu007, @alex-clawd, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide - - - - - ## v1.14.2rc1 - - [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2rc1) - - ## 변경 사항 - - ### 버그 수정 - - MCP 도구 해상도에서 순환 JSON 스키마 처리 수정 - - python-multipart를 0.0.26으로 업데이트하여 취약점 수정 - - pypdf를 6.10.1로 업데이트하여 취약점 수정 - - ### 문서 - - v1.14.2a5에 대한 변경 로그 및 버전 업데이트 - - ## 기여자 - - @greysonlalonde - - - - - ## v1.14.2a5 - - [GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2a5) - - ## 변경 사항 - - ### 문서 - - v1.14.2a4의 변경 로그 및 버전 업데이트 - - ## 기여자 - - @greysonlalonde - - - ## v1.14.2a4 diff --git a/docs/pt-BR/changelog.mdx b/docs/pt-BR/changelog.mdx index ed14c66db..a8b9bc4c2 100644 --- a/docs/pt-BR/changelog.mdx +++ b/docs/pt-BR/changelog.mdx @@ -4,137 +4,6 @@ description: "Atualizações de produto, melhorias e correções do CrewAI" icon: "clock" mode: "wide" --- - - ## v1.14.3a2 - - [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a2) - - ## O que mudou - - ### Recursos - - Adicionar suporte para bedrock V4 - - Adicionar ferramentas de sandbox Daytona para funcionalidade aprimorada - - Adicionar página 'Construir com IA' — documentação nativa de IA para agentes de codificação - - Adicionar Construir com IA à navegação Começar e arquivos de página para todos os idiomas (en, ko, pt-BR, ar) - - ### Correções de Bugs - - Corrigir a propagação de nomes implícitos @CrewBase para eventos da equipe - - Resolver problema com inicialização de lote duplicada na mesclagem de metadados de execução - - Corrigir a serialização de campos de referência de classe Task para checkpointing - - Lidar com o resultado BaseModel no loop de repetição de guardrail - - Atualizar python-dotenv para a versão >=1.2.2 para conformidade de segurança - - ### Documentação - - Atualizar changelog e versão para v1.14.3a1 - - Atualizar descrições e aplicar traduções reais - - ## Contributors - - @MatthiasHowellYopp, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @renatonitta - - - - - ## v1.14.3a1 - - [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.3a1) - - ## O que Mudou - - ### Funcionalidades - - Adicionar suporte a checkpoint e fork para agentes autônomos - - ### Correções de Bugs - - Preservar thought_signature nas chamadas da ferramenta de streaming Gemini - - Emitir task_started na retomada do fork e redesenhar a TUI de checkpoint - - Corrigir a ordem do dry-run e lidar com branch desatualizada em release do devtools - - Usar datas futuras nos testes de poda de checkpoint para evitar falhas dependentes do tempo (#5543) - - ### Documentação - - Atualizar changelog e versão para v1.14.2 - - ## Contribuidores - - @alex-clawd, @greysonlalonde - - - - - ## v1.14.2 - - [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2) - - ## O que Mudou - - ### Recursos - - Adicionar comandos de retomar, diferenciar e podar checkpoints com melhor descobribilidade. - - Adicionar o parâmetro `from_checkpoint` ao `Agent.kickoff` e métodos relacionados. - - Adicionar comandos de gerenciamento de templates para templates de projeto. - - Adicionar dicas de retomar na liberação de devtools em caso de falha. - - Adicionar CLI de validação de implantação e melhorar a ergonomia da inicialização do LLM. - - Adicionar bifurcação de checkpoints com rastreamento de linhagem. - - Enriquecer o rastreamento de tokens do LLM com tokens de raciocínio e tokens de criação de cache. - - ### Correções de Bugs - - Corrigir prompt em conflitos de branch obsoletos na liberação de devtools. - - Corrigir vulnerabilidades em `authlib`, `langchain-text-splitters` e `pypdf`. - - Restringir manipuladores de streaming para evitar contaminação de chunks entre execuções. - - Despachar checkpoints de Flow através das APIs de Flow na TUI. - - Usar glob recursivo para descoberta de checkpoints JSON. - - Lidar com esquemas JSON cíclicos na resolução de ferramentas MCP. - - Preservar os argumentos de chamada da ferramenta Bedrock removendo o padrão truthy. - - Emitir evento flow_finished após retomar HITL. - - Corrigir várias vulnerabilidades atualizando dependências, incluindo `requests`, `cryptography` e `pytest`. - - Corrigir para parar de encaminhar o modo estrito para a API Bedrock Converse. - - ### Documentação - - Documentar parâmetros ausentes e adicionar seção de Checkpointing. - - Atualizar changelog e versão para v1.14.2 e candidatos a liberação anteriores. - - Adicionar documentação da funcionalidade A2A empresarial e atualizar a documentação A2A OSS. - - ## Contribuidores - - @Yanhu007, @alex-clawd, @github-actions[bot], @greysonlalonde, @iris-clawd, @lorenzejay, @lucasgomide - - - - - ## v1.14.2rc1 - - [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2rc1) - - ## O que Mudou - - ### Correções de Bugs - - Corrigir o manuseio de esquemas JSON cíclicos na resolução da ferramenta MCP - - Corrigir vulnerabilidade atualizando python-multipart para 0.0.26 - - Corrigir vulnerabilidade atualizando pypdf para 6.10.1 - - ### Documentação - - Atualizar o changelog e a versão para v1.14.2a5 - - ## Contribuidores - - @greysonlalonde - - - - - ## v1.14.2a5 - - [Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.2a5) - - ## O que Mudou - - ### Documentação - - Atualizar changelog e versão para v1.14.2a4 - - ## Contribuidores - - @greysonlalonde - - - ## v1.14.2a4 diff --git a/lib/crewai/pyproject.toml b/lib/crewai/pyproject.toml index 001f2b8a6..cbf017801 100644 --- a/lib/crewai/pyproject.toml +++ b/lib/crewai/pyproject.toml @@ -94,6 +94,7 @@ google-genai = [ ] azure-ai-inference = [ "azure-ai-inference~=1.0.0b9", + "azure-identity>=1.17.0,<2", ] anthropic = [ "anthropic~=0.73.0", diff --git a/lib/crewai/src/crewai/llms/providers/azure/completion.py b/lib/crewai/src/crewai/llms/providers/azure/completion.py index 4b8d842a5..fd8957e08 100644 --- a/lib/crewai/src/crewai/llms/providers/azure/completion.py +++ b/lib/crewai/src/crewai/llms/providers/azure/completion.py @@ -35,6 +35,7 @@ try: ) from azure.core.credentials import ( AzureKeyCredential, + TokenCredential, ) from azure.core.exceptions import ( HttpResponseError, @@ -88,6 +89,8 @@ class AzureCompletion(BaseLLM): response_format: type[BaseModel] | None = None is_openai_model: bool = False is_azure_openai_endpoint: bool = False + azure_tenant_id: str | None = None + azure_client_id: str | None = None _client: Any = PrivateAttr(default=None) _async_client: Any = PrivateAttr(default=None) @@ -115,6 +118,8 @@ class AzureCompletion(BaseLLM): data["api_version"] = ( data.get("api_version") or os.getenv("AZURE_API_VERSION") or "2024-06-01" ) + data["azure_tenant_id"] = data.get("azure_tenant_id") or os.getenv("AZURE_TENANT_ID") + data["azure_client_id"] = data.get("azure_client_id") or os.getenv("AZURE_CLIENT_ID") # Credentials and endpoint are validated lazily in `_init_clients` # so the LLM can be constructed before deployment env vars are set. @@ -183,24 +188,89 @@ class AzureCompletion(BaseLLM): AzureCompletion._is_azure_openai_endpoint(self.endpoint) ) - if not self.api_key: - raise ValueError( - "Azure API key is required. Set AZURE_API_KEY environment " - "variable or pass api_key parameter." - ) + # Re-read identity env vars for deferred builds + if not self.azure_tenant_id: + self.azure_tenant_id = os.getenv("AZURE_TENANT_ID") + if not self.azure_client_id: + self.azure_client_id = os.getenv("AZURE_CLIENT_ID") + if not self.endpoint: raise ValueError( "Azure endpoint is required. Set AZURE_ENDPOINT environment " "variable or pass endpoint parameter." ) + + credential = self._resolve_credential() + client_kwargs: dict[str, Any] = { "endpoint": self.endpoint, - "credential": AzureKeyCredential(self.api_key), + "credential": credential, } if self.api_version: client_kwargs["api_version"] = self.api_version return client_kwargs + def _resolve_credential(self) -> AzureKeyCredential | TokenCredential: + """Resolve the Azure credential using a priority chain. + + 1. OIDC federation (WorkloadIdentityCredential) — auto-discovered + from AZURE_FEDERATED_TOKEN_FILE + AZURE_TENANT_ID + AZURE_CLIENT_ID + 2. Client secret (ClientSecretCredential) — explicit SP credentials + 3. Default chain (DefaultAzureCredential) — Managed Identity et al. + 4. API key fallback (AzureKeyCredential) — existing path + """ + federated_token_file = os.getenv("AZURE_FEDERATED_TOKEN_FILE") + client_secret = os.getenv("AZURE_CLIENT_SECRET") + + # Path 1: OIDC Workload Identity Federation + if federated_token_file and self.azure_tenant_id and self.azure_client_id: + try: + from azure.identity import WorkloadIdentityCredential + + return WorkloadIdentityCredential( + tenant_id=self.azure_tenant_id, + client_id=self.azure_client_id, + token_file_path=federated_token_file, + ) + except ImportError: + raise ImportError( + "azure-identity is required for workload identity federation. " + 'Install with: uv add "crewai[azure-ai-inference]"' + ) from None + + # Path 2: Client Secret (Service Principal) + if client_secret and self.azure_tenant_id and self.azure_client_id: + try: + from azure.identity import ClientSecretCredential + + return ClientSecretCredential( + tenant_id=self.azure_tenant_id, + client_id=self.azure_client_id, + client_secret=client_secret, + ) + except ImportError: + raise ImportError( + "azure-identity is required for service principal authentication. " + 'Install with: uv add "crewai[azure-ai-inference]"' + ) from None + + # Path 3: DefaultAzureCredential (Managed Identity, Azure CLI, etc.) + # Only attempt if azure-identity is installed and no API key is available + if not self.api_key: + try: + from azure.identity import DefaultAzureCredential + + return DefaultAzureCredential() + except ImportError: + raise ValueError( + "Azure API key is required when azure-identity is not installed. " + "Set AZURE_API_KEY environment variable, pass api_key parameter, " + 'or install azure-identity: uv add "crewai[azure-ai-inference]"' + ) from None + + # Path 4: API Key (existing path) + return AzureKeyCredential(self.api_key) + def _get_sync_client(self) -> Any: if self._client is None: self._client = self._build_sync_client() diff --git a/lib/crewai/tests/llms/azure/test_azure_credentials.py b/lib/crewai/tests/llms/azure/test_azure_credentials.py new file mode 100644 index 000000000..157e3ab80 --- /dev/null +++ b/lib/crewai/tests/llms/azure/test_azure_credentials.py @@ -0,0 +1,257 @@ +"""Tests for Azure credential resolution chain in AzureCompletion. + +Covers the four credential paths: +1. WorkloadIdentityCredential (OIDC federation) +2. ClientSecretCredential (Service Principal) +3. DefaultAzureCredential (Managed Identity / CLI fallback) +4. AzureKeyCredential (API key - existing path) +""" + +import os +from unittest.mock import patch, MagicMock + +import pytest + + +ENDPOINT = "https://test.openai.azure.com" + + +@pytest.fixture +def _clear_azure_env(monkeypatch): + """Remove all Azure env vars to start clean.""" + for key in [ + "AZURE_API_KEY", "AZURE_ENDPOINT", "AZURE_OPENAI_ENDPOINT", + "AZURE_API_BASE", "AZURE_API_VERSION", "AZURE_TENANT_ID", + "AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_FEDERATED_TOKEN_FILE", + ]: + monkeypatch.delenv(key, raising=False) + + +@pytest.mark.usefixtures("_clear_azure_env") +class TestCredentialResolution: + """Tests for AzureCompletion._resolve_credential.""" + + def test_api_key_credential_when_api_key_set(self): + """Path 4: API key produces AzureKeyCredential.""" + from crewai.llms.providers.azure.completion import AzureCompletion + from azure.core.credentials import AzureKeyCredential + + completion = AzureCompletion( + model="gpt-4", + api_key="test-key", + endpoint=ENDPOINT, + ) + cred = completion._resolve_credential() + assert isinstance(cred, AzureKeyCredential) + + def test_api_key_from_env(self, monkeypatch): + """Path 4: api_key picked up from AZURE_API_KEY env var.""" + from crewai.llms.providers.azure.completion import AzureCompletion + from azure.core.credentials import AzureKeyCredential + + monkeypatch.setenv("AZURE_API_KEY", "env-key") + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + completion = AzureCompletion(model="gpt-4") + cred = completion._resolve_credential() + assert isinstance(cred, AzureKeyCredential) + + def test_workload_identity_credential(self, monkeypatch, tmp_path): + """Path 1: OIDC federation via WorkloadIdentityCredential.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + token_file = tmp_path / "token.txt" + token_file.write_text("eyJhbGciOiJSUzI1NiJ9.test") + + monkeypatch.setenv("AZURE_FEDERATED_TOKEN_FILE", str(token_file)) + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + mock_wi_cred = MagicMock() + with patch( + "azure.identity.WorkloadIdentityCredential", + return_value=mock_wi_cred, + ) as mock_cls: + completion = AzureCompletion( + model="gpt-4", + azure_tenant_id="tenant-123", + azure_client_id="client-456", + ) + cred = completion._resolve_credential() + assert cred is mock_wi_cred + mock_cls.assert_called_once_with( + tenant_id="tenant-123", + client_id="client-456", + token_file_path=str(token_file), + ) + + def test_workload_identity_from_env_vars(self, monkeypatch, tmp_path): + """Path 1: All WI fields discovered from environment.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + token_file = tmp_path / "token.txt" + token_file.write_text("eyJhbGciOiJSUzI1NiJ9.test") + + monkeypatch.setenv("AZURE_FEDERATED_TOKEN_FILE", str(token_file)) + monkeypatch.setenv("AZURE_TENANT_ID", "env-tenant") + monkeypatch.setenv("AZURE_CLIENT_ID", "env-client") + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + mock_wi_cred = MagicMock() + with patch( + "azure.identity.WorkloadIdentityCredential", + return_value=mock_wi_cred, + ) as mock_cls: + completion = AzureCompletion(model="gpt-4") + cred = completion._resolve_credential() + assert cred is mock_wi_cred + mock_cls.assert_called_once_with( + tenant_id="env-tenant", + client_id="env-client", + token_file_path=str(token_file), + ) + + def test_client_secret_credential(self, monkeypatch): + """Path 2: Service Principal with client secret.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + monkeypatch.setenv("AZURE_CLIENT_SECRET", "sp-secret") + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + mock_cs_cred = MagicMock() + with patch( + "azure.identity.ClientSecretCredential", + return_value=mock_cs_cred, + ) as mock_cls: + completion = AzureCompletion( + model="gpt-4", + azure_tenant_id="tenant-123", + azure_client_id="client-456", + ) + cred = completion._resolve_credential() + assert cred is mock_cs_cred + mock_cls.assert_called_once_with( + tenant_id="tenant-123", + client_id="client-456", + client_secret="sp-secret", + ) + + def test_default_azure_credential_when_no_api_key(self, monkeypatch): + """Path 3: DefaultAzureCredential when no api_key and no SP/WI vars.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + mock_default_cred = MagicMock() + with patch( + "azure.identity.DefaultAzureCredential", + return_value=mock_default_cred, + ): + completion = AzureCompletion(model="gpt-4") + cred = completion._resolve_credential() + assert cred is mock_default_cred + + def test_workload_identity_takes_priority_over_api_key(self, monkeypatch, tmp_path): + """WI credential should take priority even when api_key is also set.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + token_file = tmp_path / "token.txt" + token_file.write_text("eyJhbGciOiJSUzI1NiJ9.test") + + monkeypatch.setenv("AZURE_FEDERATED_TOKEN_FILE", str(token_file)) + monkeypatch.setenv("AZURE_API_KEY", "should-not-use-this") + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + mock_wi_cred = MagicMock() + with patch( + "azure.identity.WorkloadIdentityCredential", + return_value=mock_wi_cred, + ): + completion = AzureCompletion( + model="gpt-4", + azure_tenant_id="tenant-123", + azure_client_id="client-456", + ) + cred = completion._resolve_credential() + assert cred is mock_wi_cred + + def test_client_secret_takes_priority_over_api_key(self, monkeypatch): + """SP credential should take priority over API key.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + monkeypatch.setenv("AZURE_CLIENT_SECRET", "sp-secret") + monkeypatch.setenv("AZURE_API_KEY", "should-not-use-this") + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + mock_cs_cred = MagicMock() + with patch( + "azure.identity.ClientSecretCredential", + return_value=mock_cs_cred, + ): + completion = AzureCompletion( + model="gpt-4", + azure_tenant_id="tenant-123", + azure_client_id="client-456", + ) + cred = completion._resolve_credential() + assert cred is mock_cs_cred + + def test_raises_when_no_api_key_and_no_azure_identity(self, monkeypatch): + """ValueError when no api_key and azure-identity not installed.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + with patch.dict("sys.modules", {"azure.identity": None}): + completion = AzureCompletion(model="gpt-4") + with pytest.raises(ValueError, match="Azure API key is required"): + completion._resolve_credential() + + def test_endpoint_still_required(self, monkeypatch, tmp_path): + """Endpoint is always required regardless of credential type.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + token_file = tmp_path / "token.txt" + token_file.write_text("test-jwt") + + monkeypatch.setenv("AZURE_FEDERATED_TOKEN_FILE", str(token_file)) + monkeypatch.setenv("AZURE_TENANT_ID", "tenant-123") + monkeypatch.setenv("AZURE_CLIENT_ID", "client-456") + + completion = AzureCompletion(model="gpt-4") + with pytest.raises(ValueError, match="Azure endpoint is required"): + completion._make_client_kwargs() + + def test_deferred_build_picks_up_wi_env_vars(self, monkeypatch, tmp_path): + """Env vars set after construction are picked up on deferred build.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + # Construct with endpoint only — no credentials yet + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + completion = AzureCompletion(model="gpt-4") + + # Now set WI env vars (simulating WI manager setting them before crew run) + token_file = tmp_path / "token.txt" + token_file.write_text("eyJhbGciOiJSUzI1NiJ9.deferred") + monkeypatch.setenv("AZURE_FEDERATED_TOKEN_FILE", str(token_file)) + monkeypatch.setenv("AZURE_TENANT_ID", "deferred-tenant") + monkeypatch.setenv("AZURE_CLIENT_ID", "deferred-client") + + mock_wi_cred = MagicMock() + with patch( + "azure.identity.WorkloadIdentityCredential", + return_value=mock_wi_cred, + ): + kwargs = completion._make_client_kwargs() + assert kwargs["credential"] is mock_wi_cred + + def test_make_client_kwargs_includes_api_version(self, monkeypatch): + """api_version is included in client kwargs.""" + from crewai.llms.providers.azure.completion import AzureCompletion + + monkeypatch.setenv("AZURE_API_KEY", "test-key") + monkeypatch.setenv("AZURE_ENDPOINT", ENDPOINT) + + completion = AzureCompletion(model="gpt-4", api_version="2025-01-01") + kwargs = completion._make_client_kwargs() + assert kwargs["api_version"] == "2025-01-01" + assert kwargs["endpoint"] == ENDPOINT