claude-autopm 2.8.2 → 2.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +399 -637
- package/package.json +2 -1
- package/packages/plugin-ai/LICENSE +21 -0
- package/packages/plugin-ai/README.md +316 -0
- package/packages/plugin-ai/agents/anthropic-claude-expert.md +579 -0
- package/packages/plugin-ai/agents/azure-openai-expert.md +1411 -0
- package/packages/plugin-ai/agents/gemini-api-expert.md +880 -0
- package/packages/plugin-ai/agents/google-a2a-expert.md +1445 -0
- package/packages/plugin-ai/agents/huggingface-expert.md +2131 -0
- package/packages/plugin-ai/agents/langchain-expert.md +1427 -0
- package/packages/plugin-ai/agents/langgraph-workflow-expert.md +520 -0
- package/packages/plugin-ai/agents/openai-python-expert.md +1087 -0
- package/packages/plugin-ai/commands/a2a-setup.md +886 -0
- package/packages/plugin-ai/commands/ai-model-deployment.md +481 -0
- package/packages/plugin-ai/commands/anthropic-optimize.md +793 -0
- package/packages/plugin-ai/commands/huggingface-deploy.md +789 -0
- package/packages/plugin-ai/commands/langchain-optimize.md +807 -0
- package/packages/plugin-ai/commands/llm-optimize.md +348 -0
- package/packages/plugin-ai/commands/openai-optimize.md +863 -0
- package/packages/plugin-ai/commands/rag-optimize.md +841 -0
- package/packages/plugin-ai/commands/rag-setup-scaffold.md +382 -0
- package/packages/plugin-ai/package.json +66 -0
- package/packages/plugin-ai/plugin.json +519 -0
- package/packages/plugin-ai/rules/ai-model-standards.md +449 -0
- package/packages/plugin-ai/rules/prompt-engineering-standards.md +509 -0
- package/packages/plugin-ai/scripts/examples/huggingface-inference-example.py +145 -0
- package/packages/plugin-ai/scripts/examples/langchain-rag-example.py +366 -0
- package/packages/plugin-ai/scripts/examples/mlflow-tracking-example.py +224 -0
- package/packages/plugin-ai/scripts/examples/openai-chat-example.py +425 -0
- package/packages/plugin-cloud/README.md +268 -0
- package/packages/plugin-cloud/agents/README.md +55 -0
- package/packages/plugin-cloud/agents/aws-cloud-architect.md +521 -0
- package/packages/plugin-cloud/agents/azure-cloud-architect.md +436 -0
- package/packages/plugin-cloud/agents/gcp-cloud-architect.md +385 -0
- package/packages/plugin-cloud/agents/gcp-cloud-functions-engineer.md +306 -0
- package/packages/plugin-cloud/agents/gemini-api-expert.md +880 -0
- package/packages/plugin-cloud/agents/kubernetes-orchestrator.md +566 -0
- package/packages/plugin-cloud/agents/openai-python-expert.md +1087 -0
- package/packages/plugin-cloud/agents/terraform-infrastructure-expert.md +454 -0
- package/packages/plugin-cloud/commands/cloud-cost-optimize.md +243 -0
- package/packages/plugin-cloud/commands/cloud-validate.md +196 -0
- package/packages/plugin-cloud/commands/infra-deploy.md +38 -0
- package/packages/plugin-cloud/commands/k8s-deploy.md +37 -0
- package/packages/plugin-cloud/commands/ssh-security.md +65 -0
- package/packages/plugin-cloud/commands/traefik-setup.md +65 -0
- package/packages/plugin-cloud/hooks/pre-cloud-deploy.js +456 -0
- package/packages/plugin-cloud/package.json +64 -0
- package/packages/plugin-cloud/plugin.json +338 -0
- package/packages/plugin-cloud/rules/cloud-security-compliance.md +313 -0
- package/packages/plugin-cloud/rules/infrastructure-pipeline.md +128 -0
- package/packages/plugin-cloud/scripts/examples/aws-validate.sh +30 -0
- package/packages/plugin-cloud/scripts/examples/azure-setup.sh +33 -0
- package/packages/plugin-cloud/scripts/examples/gcp-setup.sh +39 -0
- package/packages/plugin-cloud/scripts/examples/k8s-validate.sh +40 -0
- package/packages/plugin-cloud/scripts/examples/terraform-init.sh +26 -0
- package/packages/plugin-core/README.md +274 -0
- package/packages/plugin-core/agents/core/agent-manager.md +296 -0
- package/packages/plugin-core/agents/core/code-analyzer.md +131 -0
- package/packages/plugin-core/agents/core/file-analyzer.md +162 -0
- package/packages/plugin-core/agents/core/test-runner.md +200 -0
- package/packages/plugin-core/commands/code-rabbit.md +128 -0
- package/packages/plugin-core/commands/prompt.md +9 -0
- package/packages/plugin-core/commands/re-init.md +9 -0
- package/packages/plugin-core/hooks/context7-reminder.md +29 -0
- package/packages/plugin-core/hooks/enforce-agents.js +125 -0
- package/packages/plugin-core/hooks/enforce-agents.sh +35 -0
- package/packages/plugin-core/hooks/pre-agent-context7.js +224 -0
- package/packages/plugin-core/hooks/pre-command-context7.js +229 -0
- package/packages/plugin-core/hooks/strict-enforce-agents.sh +39 -0
- package/packages/plugin-core/hooks/test-hook.sh +21 -0
- package/packages/plugin-core/hooks/unified-context7-enforcement.sh +38 -0
- package/packages/plugin-core/package.json +45 -0
- package/packages/plugin-core/plugin.json +387 -0
- package/packages/plugin-core/rules/agent-coordination.md +549 -0
- package/packages/plugin-core/rules/agent-mandatory.md +170 -0
- package/packages/plugin-core/rules/ai-integration-patterns.md +219 -0
- package/packages/plugin-core/rules/command-pipelines.md +208 -0
- package/packages/plugin-core/rules/context-optimization.md +176 -0
- package/packages/plugin-core/rules/context7-enforcement.md +327 -0
- package/packages/plugin-core/rules/datetime.md +122 -0
- package/packages/plugin-core/rules/definition-of-done.md +272 -0
- package/packages/plugin-core/rules/development-environments.md +19 -0
- package/packages/plugin-core/rules/development-workflow.md +198 -0
- package/packages/plugin-core/rules/framework-path-rules.md +180 -0
- package/packages/plugin-core/rules/frontmatter-operations.md +64 -0
- package/packages/plugin-core/rules/git-strategy.md +237 -0
- package/packages/plugin-core/rules/golden-rules.md +181 -0
- package/packages/plugin-core/rules/naming-conventions.md +111 -0
- package/packages/plugin-core/rules/no-pr-workflow.md +183 -0
- package/packages/plugin-core/rules/performance-guidelines.md +403 -0
- package/packages/plugin-core/rules/pipeline-mandatory.md +109 -0
- package/packages/plugin-core/rules/security-checklist.md +318 -0
- package/packages/plugin-core/rules/standard-patterns.md +197 -0
- package/packages/plugin-core/rules/strip-frontmatter.md +85 -0
- package/packages/plugin-core/rules/tdd.enforcement.md +103 -0
- package/packages/plugin-core/rules/use-ast-grep.md +113 -0
- package/packages/plugin-core/scripts/lib/datetime-utils.sh +254 -0
- package/packages/plugin-core/scripts/lib/frontmatter-utils.sh +294 -0
- package/packages/plugin-core/scripts/lib/github-utils.sh +221 -0
- package/packages/plugin-core/scripts/lib/logging-utils.sh +199 -0
- package/packages/plugin-core/scripts/lib/validation-utils.sh +339 -0
- package/packages/plugin-core/scripts/mcp/add.sh +7 -0
- package/packages/plugin-core/scripts/mcp/disable.sh +12 -0
- package/packages/plugin-core/scripts/mcp/enable.sh +12 -0
- package/packages/plugin-core/scripts/mcp/list.sh +7 -0
- package/packages/plugin-core/scripts/mcp/sync.sh +8 -0
- package/packages/plugin-data/README.md +315 -0
- package/packages/plugin-data/agents/airflow-orchestration-expert.md +158 -0
- package/packages/plugin-data/agents/kedro-pipeline-expert.md +304 -0
- package/packages/plugin-data/agents/langgraph-workflow-expert.md +530 -0
- package/packages/plugin-data/commands/airflow-dag-scaffold.md +413 -0
- package/packages/plugin-data/commands/kafka-pipeline-scaffold.md +503 -0
- package/packages/plugin-data/package.json +66 -0
- package/packages/plugin-data/plugin.json +294 -0
- package/packages/plugin-data/rules/data-quality-standards.md +373 -0
- package/packages/plugin-data/rules/etl-pipeline-standards.md +255 -0
- package/packages/plugin-data/scripts/examples/airflow-dag-example.py +245 -0
- package/packages/plugin-data/scripts/examples/dbt-transform-example.sql +238 -0
- package/packages/plugin-data/scripts/examples/kafka-streaming-example.py +257 -0
- package/packages/plugin-data/scripts/examples/pandas-etl-example.py +332 -0
- package/packages/plugin-databases/README.md +330 -0
- package/packages/plugin-databases/agents/README.md +50 -0
- package/packages/plugin-databases/agents/bigquery-expert.md +401 -0
- package/packages/plugin-databases/agents/cosmosdb-expert.md +375 -0
- package/packages/plugin-databases/agents/mongodb-expert.md +407 -0
- package/packages/plugin-databases/agents/postgresql-expert.md +329 -0
- package/packages/plugin-databases/agents/redis-expert.md +74 -0
- package/packages/plugin-databases/commands/db-optimize.md +612 -0
- package/packages/plugin-databases/package.json +60 -0
- package/packages/plugin-databases/plugin.json +237 -0
- package/packages/plugin-databases/rules/database-management-strategy.md +146 -0
- package/packages/plugin-databases/rules/database-pipeline.md +316 -0
- package/packages/plugin-databases/scripts/examples/bigquery-cost-analyze.sh +160 -0
- package/packages/plugin-databases/scripts/examples/cosmosdb-ru-optimize.sh +163 -0
- package/packages/plugin-databases/scripts/examples/mongodb-shard-check.sh +120 -0
- package/packages/plugin-databases/scripts/examples/postgres-index-analyze.sh +95 -0
- package/packages/plugin-databases/scripts/examples/redis-cache-stats.sh +121 -0
- package/packages/plugin-devops/README.md +367 -0
- package/packages/plugin-devops/agents/README.md +52 -0
- package/packages/plugin-devops/agents/azure-devops-specialist.md +308 -0
- package/packages/plugin-devops/agents/docker-containerization-expert.md +298 -0
- package/packages/plugin-devops/agents/github-operations-specialist.md +335 -0
- package/packages/plugin-devops/agents/mcp-context-manager.md +319 -0
- package/packages/plugin-devops/agents/observability-engineer.md +574 -0
- package/packages/plugin-devops/agents/ssh-operations-expert.md +1093 -0
- package/packages/plugin-devops/agents/traefik-proxy-expert.md +444 -0
- package/packages/plugin-devops/commands/ci-pipeline-create.md +581 -0
- package/packages/plugin-devops/commands/docker-optimize.md +493 -0
- package/packages/plugin-devops/commands/workflow-create.md +42 -0
- package/packages/plugin-devops/hooks/pre-docker-build.js +472 -0
- package/packages/plugin-devops/package.json +61 -0
- package/packages/plugin-devops/plugin.json +302 -0
- package/packages/plugin-devops/rules/ci-cd-kubernetes-strategy.md +25 -0
- package/packages/plugin-devops/rules/devops-troubleshooting-playbook.md +450 -0
- package/packages/plugin-devops/rules/docker-first-development.md +404 -0
- package/packages/plugin-devops/rules/github-operations.md +92 -0
- package/packages/plugin-devops/scripts/examples/docker-build-multistage.sh +43 -0
- package/packages/plugin-devops/scripts/examples/docker-compose-validate.sh +74 -0
- package/packages/plugin-devops/scripts/examples/github-workflow-validate.sh +48 -0
- package/packages/plugin-devops/scripts/examples/prometheus-health-check.sh +58 -0
- package/packages/plugin-devops/scripts/examples/ssh-key-setup.sh +74 -0
- package/packages/plugin-frameworks/README.md +309 -0
- package/packages/plugin-frameworks/agents/README.md +64 -0
- package/packages/plugin-frameworks/agents/e2e-test-engineer.md +579 -0
- package/packages/plugin-frameworks/agents/nats-messaging-expert.md +254 -0
- package/packages/plugin-frameworks/agents/react-frontend-engineer.md +393 -0
- package/packages/plugin-frameworks/agents/react-ui-expert.md +226 -0
- package/packages/plugin-frameworks/agents/tailwindcss-expert.md +1021 -0
- package/packages/plugin-frameworks/agents/ux-design-expert.md +244 -0
- package/packages/plugin-frameworks/commands/app-scaffold.md +50 -0
- package/packages/plugin-frameworks/commands/nextjs-optimize.md +692 -0
- package/packages/plugin-frameworks/commands/react-optimize.md +583 -0
- package/packages/plugin-frameworks/commands/tailwind-system.md +64 -0
- package/packages/plugin-frameworks/package.json +59 -0
- package/packages/plugin-frameworks/plugin.json +224 -0
- package/packages/plugin-frameworks/rules/performance-guidelines.md +403 -0
- package/packages/plugin-frameworks/rules/ui-development-standards.md +281 -0
- package/packages/plugin-frameworks/rules/ui-framework-rules.md +151 -0
- package/packages/plugin-frameworks/scripts/examples/react-component-perf.sh +34 -0
- package/packages/plugin-frameworks/scripts/examples/tailwind-optimize.sh +44 -0
- package/packages/plugin-frameworks/scripts/examples/vue-composition-check.sh +41 -0
- package/packages/plugin-languages/README.md +333 -0
- package/packages/plugin-languages/agents/README.md +50 -0
- package/packages/plugin-languages/agents/bash-scripting-expert.md +541 -0
- package/packages/plugin-languages/agents/javascript-frontend-engineer.md +197 -0
- package/packages/plugin-languages/agents/nodejs-backend-engineer.md +226 -0
- package/packages/plugin-languages/agents/python-backend-engineer.md +214 -0
- package/packages/plugin-languages/agents/python-backend-expert.md +289 -0
- package/packages/plugin-languages/commands/javascript-optimize.md +636 -0
- package/packages/plugin-languages/commands/nodejs-api-scaffold.md +341 -0
- package/packages/plugin-languages/commands/nodejs-optimize.md +689 -0
- package/packages/plugin-languages/commands/python-api-scaffold.md +261 -0
- package/packages/plugin-languages/commands/python-optimize.md +593 -0
- package/packages/plugin-languages/package.json +65 -0
- package/packages/plugin-languages/plugin.json +265 -0
- package/packages/plugin-languages/rules/code-quality-standards.md +496 -0
- package/packages/plugin-languages/rules/testing-standards.md +768 -0
- package/packages/plugin-languages/scripts/examples/bash-production-script.sh +520 -0
- package/packages/plugin-languages/scripts/examples/javascript-es6-patterns.js +291 -0
- package/packages/plugin-languages/scripts/examples/nodejs-async-iteration.js +360 -0
- package/packages/plugin-languages/scripts/examples/python-async-patterns.py +289 -0
- package/packages/plugin-languages/scripts/examples/typescript-patterns.ts +432 -0
- package/packages/plugin-ml/README.md +430 -0
- package/packages/plugin-ml/agents/automl-expert.md +326 -0
- package/packages/plugin-ml/agents/computer-vision-expert.md +550 -0
- package/packages/plugin-ml/agents/gradient-boosting-expert.md +455 -0
- package/packages/plugin-ml/agents/neural-network-architect.md +1228 -0
- package/packages/plugin-ml/agents/nlp-transformer-expert.md +584 -0
- package/packages/plugin-ml/agents/pytorch-expert.md +412 -0
- package/packages/plugin-ml/agents/reinforcement-learning-expert.md +2088 -0
- package/packages/plugin-ml/agents/scikit-learn-expert.md +228 -0
- package/packages/plugin-ml/agents/tensorflow-keras-expert.md +509 -0
- package/packages/plugin-ml/agents/time-series-expert.md +303 -0
- package/packages/plugin-ml/commands/ml-automl.md +572 -0
- package/packages/plugin-ml/commands/ml-train-optimize.md +657 -0
- package/packages/plugin-ml/package.json +52 -0
- package/packages/plugin-ml/plugin.json +338 -0
- package/packages/plugin-pm/README.md +368 -0
- package/packages/plugin-pm/claudeautopm-plugin-pm-2.0.0.tgz +0 -0
- package/packages/plugin-pm/commands/azure/COMMANDS.md +107 -0
- package/packages/plugin-pm/commands/azure/COMMAND_MAPPING.md +252 -0
- package/packages/plugin-pm/commands/azure/INTEGRATION_FIX.md +103 -0
- package/packages/plugin-pm/commands/azure/README.md +246 -0
- package/packages/plugin-pm/commands/azure/active-work.md +198 -0
- package/packages/plugin-pm/commands/azure/aliases.md +143 -0
- package/packages/plugin-pm/commands/azure/blocked-items.md +287 -0
- package/packages/plugin-pm/commands/azure/clean.md +93 -0
- package/packages/plugin-pm/commands/azure/docs-query.md +48 -0
- package/packages/plugin-pm/commands/azure/feature-decompose.md +380 -0
- package/packages/plugin-pm/commands/azure/feature-list.md +61 -0
- package/packages/plugin-pm/commands/azure/feature-new.md +115 -0
- package/packages/plugin-pm/commands/azure/feature-show.md +205 -0
- package/packages/plugin-pm/commands/azure/feature-start.md +130 -0
- package/packages/plugin-pm/commands/azure/fix-integration-example.md +93 -0
- package/packages/plugin-pm/commands/azure/help.md +150 -0
- package/packages/plugin-pm/commands/azure/import-us.md +269 -0
- package/packages/plugin-pm/commands/azure/init.md +211 -0
- package/packages/plugin-pm/commands/azure/next-task.md +262 -0
- package/packages/plugin-pm/commands/azure/search.md +160 -0
- package/packages/plugin-pm/commands/azure/sprint-status.md +235 -0
- package/packages/plugin-pm/commands/azure/standup.md +260 -0
- package/packages/plugin-pm/commands/azure/sync-all.md +99 -0
- package/packages/plugin-pm/commands/azure/task-analyze.md +186 -0
- package/packages/plugin-pm/commands/azure/task-close.md +329 -0
- package/packages/plugin-pm/commands/azure/task-edit.md +145 -0
- package/packages/plugin-pm/commands/azure/task-list.md +263 -0
- package/packages/plugin-pm/commands/azure/task-new.md +84 -0
- package/packages/plugin-pm/commands/azure/task-reopen.md +79 -0
- package/packages/plugin-pm/commands/azure/task-show.md +126 -0
- package/packages/plugin-pm/commands/azure/task-start.md +301 -0
- package/packages/plugin-pm/commands/azure/task-status.md +65 -0
- package/packages/plugin-pm/commands/azure/task-sync.md +67 -0
- package/packages/plugin-pm/commands/azure/us-edit.md +164 -0
- package/packages/plugin-pm/commands/azure/us-list.md +202 -0
- package/packages/plugin-pm/commands/azure/us-new.md +265 -0
- package/packages/plugin-pm/commands/azure/us-parse.md +253 -0
- package/packages/plugin-pm/commands/azure/us-show.md +188 -0
- package/packages/plugin-pm/commands/azure/us-status.md +320 -0
- package/packages/plugin-pm/commands/azure/validate.md +86 -0
- package/packages/plugin-pm/commands/azure/work-item-sync.md +47 -0
- package/packages/plugin-pm/commands/blocked.md +28 -0
- package/packages/plugin-pm/commands/clean.md +119 -0
- package/packages/plugin-pm/commands/context-create.md +136 -0
- package/packages/plugin-pm/commands/context-prime.md +170 -0
- package/packages/plugin-pm/commands/context-update.md +292 -0
- package/packages/plugin-pm/commands/context.md +28 -0
- package/packages/plugin-pm/commands/epic-close.md +86 -0
- package/packages/plugin-pm/commands/epic-decompose.md +370 -0
- package/packages/plugin-pm/commands/epic-edit.md +83 -0
- package/packages/plugin-pm/commands/epic-list.md +30 -0
- package/packages/plugin-pm/commands/epic-merge.md +222 -0
- package/packages/plugin-pm/commands/epic-oneshot.md +119 -0
- package/packages/plugin-pm/commands/epic-refresh.md +119 -0
- package/packages/plugin-pm/commands/epic-show.md +28 -0
- package/packages/plugin-pm/commands/epic-split.md +120 -0
- package/packages/plugin-pm/commands/epic-start.md +195 -0
- package/packages/plugin-pm/commands/epic-status.md +28 -0
- package/packages/plugin-pm/commands/epic-sync-modular.md +338 -0
- package/packages/plugin-pm/commands/epic-sync-original.md +473 -0
- package/packages/plugin-pm/commands/epic-sync.md +486 -0
- package/packages/plugin-pm/commands/github/workflow-create.md +42 -0
- package/packages/plugin-pm/commands/help.md +28 -0
- package/packages/plugin-pm/commands/import.md +115 -0
- package/packages/plugin-pm/commands/in-progress.md +28 -0
- package/packages/plugin-pm/commands/init.md +28 -0
- package/packages/plugin-pm/commands/issue-analyze.md +202 -0
- package/packages/plugin-pm/commands/issue-close.md +119 -0
- package/packages/plugin-pm/commands/issue-edit.md +93 -0
- package/packages/plugin-pm/commands/issue-reopen.md +87 -0
- package/packages/plugin-pm/commands/issue-show.md +41 -0
- package/packages/plugin-pm/commands/issue-start.md +234 -0
- package/packages/plugin-pm/commands/issue-status.md +95 -0
- package/packages/plugin-pm/commands/issue-sync.md +411 -0
- package/packages/plugin-pm/commands/next.md +28 -0
- package/packages/plugin-pm/commands/prd-edit.md +82 -0
- package/packages/plugin-pm/commands/prd-list.md +28 -0
- package/packages/plugin-pm/commands/prd-new.md +55 -0
- package/packages/plugin-pm/commands/prd-parse.md +42 -0
- package/packages/plugin-pm/commands/prd-status.md +28 -0
- package/packages/plugin-pm/commands/search.md +28 -0
- package/packages/plugin-pm/commands/standup.md +28 -0
- package/packages/plugin-pm/commands/status.md +28 -0
- package/packages/plugin-pm/commands/sync.md +99 -0
- package/packages/plugin-pm/commands/test-reference-update.md +151 -0
- package/packages/plugin-pm/commands/validate.md +28 -0
- package/packages/plugin-pm/commands/what-next.md +28 -0
- package/packages/plugin-pm/package.json +57 -0
- package/packages/plugin-pm/plugin.json +503 -0
- package/packages/plugin-pm/scripts/pm/analytics.js +425 -0
- package/packages/plugin-pm/scripts/pm/blocked.js +164 -0
- package/packages/plugin-pm/scripts/pm/blocked.sh +78 -0
- package/packages/plugin-pm/scripts/pm/clean.js +464 -0
- package/packages/plugin-pm/scripts/pm/context-create.js +216 -0
- package/packages/plugin-pm/scripts/pm/context-prime.js +335 -0
- package/packages/plugin-pm/scripts/pm/context-update.js +344 -0
- package/packages/plugin-pm/scripts/pm/context.js +338 -0
- package/packages/plugin-pm/scripts/pm/epic-close.js +347 -0
- package/packages/plugin-pm/scripts/pm/epic-edit.js +382 -0
- package/packages/plugin-pm/scripts/pm/epic-list.js +273 -0
- package/packages/plugin-pm/scripts/pm/epic-list.sh +109 -0
- package/packages/plugin-pm/scripts/pm/epic-show.js +291 -0
- package/packages/plugin-pm/scripts/pm/epic-show.sh +105 -0
- package/packages/plugin-pm/scripts/pm/epic-split.js +522 -0
- package/packages/plugin-pm/scripts/pm/epic-start/epic-start.js +183 -0
- package/packages/plugin-pm/scripts/pm/epic-start/epic-start.sh +94 -0
- package/packages/plugin-pm/scripts/pm/epic-status.js +291 -0
- package/packages/plugin-pm/scripts/pm/epic-status.sh +104 -0
- package/packages/plugin-pm/scripts/pm/epic-sync/README.md +208 -0
- package/packages/plugin-pm/scripts/pm/epic-sync/create-epic-issue.sh +77 -0
- package/packages/plugin-pm/scripts/pm/epic-sync/create-task-issues.sh +86 -0
- package/packages/plugin-pm/scripts/pm/epic-sync/update-epic-file.sh +79 -0
- package/packages/plugin-pm/scripts/pm/epic-sync/update-references.sh +89 -0
- package/packages/plugin-pm/scripts/pm/epic-sync.sh +137 -0
- package/packages/plugin-pm/scripts/pm/help.js +92 -0
- package/packages/plugin-pm/scripts/pm/help.sh +90 -0
- package/packages/plugin-pm/scripts/pm/in-progress.js +178 -0
- package/packages/plugin-pm/scripts/pm/in-progress.sh +93 -0
- package/packages/plugin-pm/scripts/pm/init.js +321 -0
- package/packages/plugin-pm/scripts/pm/init.sh +178 -0
- package/packages/plugin-pm/scripts/pm/issue-close.js +232 -0
- package/packages/plugin-pm/scripts/pm/issue-edit.js +310 -0
- package/packages/plugin-pm/scripts/pm/issue-show.js +272 -0
- package/packages/plugin-pm/scripts/pm/issue-start.js +181 -0
- package/packages/plugin-pm/scripts/pm/issue-sync/format-comment.sh +468 -0
- package/packages/plugin-pm/scripts/pm/issue-sync/gather-updates.sh +460 -0
- package/packages/plugin-pm/scripts/pm/issue-sync/post-comment.sh +330 -0
- package/packages/plugin-pm/scripts/pm/issue-sync/preflight-validation.sh +348 -0
- package/packages/plugin-pm/scripts/pm/issue-sync/update-frontmatter.sh +387 -0
- package/packages/plugin-pm/scripts/pm/lib/README.md +85 -0
- package/packages/plugin-pm/scripts/pm/lib/epic-discovery.js +119 -0
- package/packages/plugin-pm/scripts/pm/lib/logger.js +78 -0
- package/packages/plugin-pm/scripts/pm/next.js +189 -0
- package/packages/plugin-pm/scripts/pm/next.sh +72 -0
- package/packages/plugin-pm/scripts/pm/optimize.js +407 -0
- package/packages/plugin-pm/scripts/pm/pr-create.js +337 -0
- package/packages/plugin-pm/scripts/pm/pr-list.js +257 -0
- package/packages/plugin-pm/scripts/pm/prd-list.js +242 -0
- package/packages/plugin-pm/scripts/pm/prd-list.sh +103 -0
- package/packages/plugin-pm/scripts/pm/prd-new.js +684 -0
- package/packages/plugin-pm/scripts/pm/prd-parse.js +547 -0
- package/packages/plugin-pm/scripts/pm/prd-status.js +152 -0
- package/packages/plugin-pm/scripts/pm/prd-status.sh +63 -0
- package/packages/plugin-pm/scripts/pm/release.js +460 -0
- package/packages/plugin-pm/scripts/pm/search.js +192 -0
- package/packages/plugin-pm/scripts/pm/search.sh +89 -0
- package/packages/plugin-pm/scripts/pm/standup.js +362 -0
- package/packages/plugin-pm/scripts/pm/standup.sh +95 -0
- package/packages/plugin-pm/scripts/pm/status.js +148 -0
- package/packages/plugin-pm/scripts/pm/status.sh +59 -0
- package/packages/plugin-pm/scripts/pm/sync-batch.js +337 -0
- package/packages/plugin-pm/scripts/pm/sync.js +343 -0
- package/packages/plugin-pm/scripts/pm/template-list.js +141 -0
- package/packages/plugin-pm/scripts/pm/template-new.js +366 -0
- package/packages/plugin-pm/scripts/pm/validate.js +274 -0
- package/packages/plugin-pm/scripts/pm/validate.sh +106 -0
- package/packages/plugin-pm/scripts/pm/what-next.js +660 -0
- package/packages/plugin-testing/README.md +401 -0
- package/packages/plugin-testing/agents/frontend-testing-engineer.md +768 -0
- package/packages/plugin-testing/commands/jest-optimize.md +800 -0
- package/packages/plugin-testing/commands/playwright-optimize.md +887 -0
- package/packages/plugin-testing/commands/test-coverage.md +512 -0
- package/packages/plugin-testing/commands/test-performance.md +1041 -0
- package/packages/plugin-testing/commands/test-setup.md +414 -0
- package/packages/plugin-testing/package.json +40 -0
- package/packages/plugin-testing/plugin.json +197 -0
- package/packages/plugin-testing/rules/test-coverage-requirements.md +581 -0
- package/packages/plugin-testing/rules/testing-standards.md +529 -0
- package/packages/plugin-testing/scripts/examples/react-testing-example.test.jsx +460 -0
- package/packages/plugin-testing/scripts/examples/vitest-config-example.js +352 -0
- package/packages/plugin-testing/scripts/examples/vue-testing-example.test.js +586 -0
|
@@ -0,0 +1,1411 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: azure-openai-expert
|
|
3
|
+
description: Use this agent for Azure OpenAI Service integration including enterprise deployments, managed endpoints, compliance features, and hybrid cloud architectures. Expert in deployment management, Azure AD authentication, private endpoints, content filtering, cost allocation, and multi-tenant patterns. Perfect for building enterprise AI-powered applications with Azure's managed OpenAI service following Microsoft's best practices.
|
|
4
|
+
tools: Glob, Grep, LS, Read, WebFetch, TodoWrite, WebSearch, Edit, Write, MultiEdit, Bash, Task, Agent
|
|
5
|
+
model: inherit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Azure OpenAI Expert Agent
|
|
9
|
+
|
|
10
|
+
## Test-Driven Development (TDD) Methodology
|
|
11
|
+
|
|
12
|
+
**MANDATORY**: Follow strict TDD principles for all development:
|
|
13
|
+
1. **Write failing tests FIRST** - Before implementing any functionality
|
|
14
|
+
2. **Red-Green-Refactor cycle** - Test fails → Make it pass → Improve code
|
|
15
|
+
3. **One test at a time** - Focus on small, incremental development
|
|
16
|
+
4. **100% coverage for new code** - All new features must have complete test coverage
|
|
17
|
+
5. **Tests as documentation** - Tests should clearly document expected behavior
|
|
18
|
+
|
|
19
|
+
You are an Azure OpenAI Service specialist focused on building enterprise-grade AI applications using Microsoft's managed OpenAI platform. Your mission is to leverage Azure's enterprise features for compliance, security, governance, and scalable AI deployments.
|
|
20
|
+
|
|
21
|
+
## Documentation Access via MCP Context7
|
|
22
|
+
|
|
23
|
+
Before implementing any Azure OpenAI solution, access live documentation through the MCP context7 integration:
|
|
24
|
+
|
|
25
|
+
- **Azure OpenAI Service**: Latest features, limits, and enterprise capabilities
|
|
26
|
+
- **Azure SDK for Python**: Official Azure client libraries and patterns
|
|
27
|
+
- **OpenAI Compatibility**: Understanding differences from OpenAI API
|
|
28
|
+
- **Enterprise Security**: Azure AD, RBAC, managed identities, private endpoints
|
|
29
|
+
- **Compliance & Governance**: Data residency, compliance certifications, audit logging
|
|
30
|
+
|
|
31
|
+
**Documentation Queries:**
|
|
32
|
+
- `mcp://context7/azure/azure-sdk-for-python/openai` - Azure OpenAI SDK documentation
|
|
33
|
+
- `mcp://context7/websites/azure/openai-service` - Azure OpenAI Service official docs
|
|
34
|
+
- `mcp://context7/openai/openai-python` - OpenAI Python patterns (for compatibility)
|
|
35
|
+
- `mcp://context7/azure/security/managed-identity` - Azure managed identity patterns
|
|
36
|
+
- `mcp://context7/azure/networking/private-endpoints` - Azure private endpoint configuration
|
|
37
|
+
- `mcp://context7/azure/monitoring/application-insights` - Azure monitoring integration
|
|
38
|
+
|
|
39
|
+
**Why These Queries are Required:**
|
|
40
|
+
- Ensures latest Azure-specific features and API changes are incorporated
|
|
41
|
+
- Validates enterprise security patterns against Microsoft best practices
|
|
42
|
+
- Prevents compatibility issues between Azure and OpenAI implementations
|
|
43
|
+
- Ensures compliance with Azure governance and cost management patterns
|
|
44
|
+
- Provides up-to-date regional availability and quota information
|
|
45
|
+
|
|
46
|
+
## Core Responsibilities
|
|
47
|
+
|
|
48
|
+
1. **Azure-Specific Deployment Management**
|
|
49
|
+
- Create and manage Azure OpenAI deployments (not models)
|
|
50
|
+
- Configure deployment capacity and scaling
|
|
51
|
+
- Implement regional failover strategies
|
|
52
|
+
- Manage API version compatibility
|
|
53
|
+
|
|
54
|
+
2. **Enterprise Security & Identity**
|
|
55
|
+
- Azure AD authentication integration
|
|
56
|
+
- Managed identity configuration (system/user-assigned)
|
|
57
|
+
- Private endpoint and VNet integration
|
|
58
|
+
- RBAC role assignments
|
|
59
|
+
- Key Vault integration for secrets
|
|
60
|
+
|
|
61
|
+
3. **Compliance & Governance**
|
|
62
|
+
- Data residency and regional deployment
|
|
63
|
+
- Content filtering and safety configuration
|
|
64
|
+
- Azure Policy compliance
|
|
65
|
+
- Audit logging with Azure Monitor
|
|
66
|
+
- Cost allocation and chargeback
|
|
67
|
+
|
|
68
|
+
4. **Multi-Tenant Architecture**
|
|
69
|
+
- Tenant isolation strategies
|
|
70
|
+
- Cost allocation per tenant/project
|
|
71
|
+
- Quota management across tenants
|
|
72
|
+
- Resource organization (resource groups, subscriptions)
|
|
73
|
+
|
|
74
|
+
5. **Hybrid Cloud Patterns**
|
|
75
|
+
- On-premises integration with Azure Arc
|
|
76
|
+
- ExpressRoute connectivity
|
|
77
|
+
- Hybrid identity federation
|
|
78
|
+
- Data sovereignty requirements
|
|
79
|
+
|
|
80
|
+
## Key Differences from OpenAI API
|
|
81
|
+
|
|
82
|
+
### Deployment vs Model Selection
|
|
83
|
+
|
|
84
|
+
**OpenAI API Pattern:**
|
|
85
|
+
```python
|
|
86
|
+
# Direct model selection
|
|
87
|
+
response = openai.chat.completions.create(
|
|
88
|
+
model="gpt-4", # ❌ NOT supported in Azure
|
|
89
|
+
messages=messages
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Azure OpenAI Pattern:**
|
|
94
|
+
```python
|
|
95
|
+
# Use deployment name (configured in Azure Portal)
|
|
96
|
+
response = client.chat.completions.create(
|
|
97
|
+
model="gpt-4-deployment", # ✅ Deployment name, not model name
|
|
98
|
+
messages=messages
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### API Version Management
|
|
103
|
+
|
|
104
|
+
**Azure requires explicit API versions:**
|
|
105
|
+
```python
|
|
106
|
+
from openai import AzureOpenAI
|
|
107
|
+
|
|
108
|
+
client = AzureOpenAI(
|
|
109
|
+
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
|
|
110
|
+
api_version="2024-02-15-preview", # ✅ REQUIRED in Azure
|
|
111
|
+
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Regional Endpoints
|
|
116
|
+
|
|
117
|
+
**Azure uses region-specific endpoints:**
|
|
118
|
+
```
|
|
119
|
+
# OpenAI
|
|
120
|
+
https://api.openai.com/v1
|
|
121
|
+
|
|
122
|
+
# Azure OpenAI
|
|
123
|
+
https://{resource-name}.openai.azure.com/
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Azure OpenAI SDK Setup and Configuration
|
|
127
|
+
|
|
128
|
+
### Installation and Environment Setup
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
# pip install openai azure-identity azure-keyvault-secrets python-dotenv pydantic
|
|
132
|
+
|
|
133
|
+
import os
|
|
134
|
+
from typing import List, Optional, Dict, Any, AsyncGenerator
|
|
135
|
+
from dataclasses import dataclass, field
|
|
136
|
+
from enum import Enum
|
|
137
|
+
import logging
|
|
138
|
+
from datetime import datetime
|
|
139
|
+
from openai import AzureOpenAI, AsyncAzureOpenAI
|
|
140
|
+
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
|
|
141
|
+
from azure.keyvault.secrets import SecretClient
|
|
142
|
+
from azure.core.exceptions import AzureError
|
|
143
|
+
import asyncio
|
|
144
|
+
|
|
145
|
+
# Configuration with Azure-specific settings
|
|
146
|
+
@dataclass
|
|
147
|
+
class AzureOpenAIConfig:
|
|
148
|
+
"""Azure OpenAI Service Configuration"""
|
|
149
|
+
# Authentication
|
|
150
|
+
api_key: Optional[str] = None # Use API key OR managed identity
|
|
151
|
+
use_managed_identity: bool = True # Preferred for production
|
|
152
|
+
credential: Optional[Any] = None # Azure credential object
|
|
153
|
+
|
|
154
|
+
# Azure-specific
|
|
155
|
+
azure_endpoint: str = "" # https://{resource-name}.openai.azure.com
|
|
156
|
+
api_version: str = "2024-02-15-preview" # Check docs for latest
|
|
157
|
+
azure_deployment: str = "gpt-4-deployment" # Your deployment name
|
|
158
|
+
|
|
159
|
+
# Optional: Key Vault integration
|
|
160
|
+
key_vault_url: Optional[str] = None
|
|
161
|
+
api_key_secret_name: str = "azure-openai-api-key"
|
|
162
|
+
|
|
163
|
+
# Connection settings
|
|
164
|
+
max_retries: int = 3
|
|
165
|
+
timeout: float = 60.0
|
|
166
|
+
|
|
167
|
+
# Model configuration
|
|
168
|
+
temperature: float = 0.1
|
|
169
|
+
max_tokens: Optional[int] = None
|
|
170
|
+
|
|
171
|
+
# Compliance
|
|
172
|
+
enable_content_filter: bool = True
|
|
173
|
+
data_residency_region: str = "eastus" # Ensure compliance
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class AzureDeploymentType(Enum):
|
|
177
|
+
"""Available deployment types in Azure OpenAI"""
|
|
178
|
+
GPT_4 = "gpt-4"
|
|
179
|
+
GPT_4_32K = "gpt-4-32k"
|
|
180
|
+
GPT_4_TURBO = "gpt-4-turbo"
|
|
181
|
+
GPT_35_TURBO = "gpt-35-turbo"
|
|
182
|
+
GPT_35_TURBO_16K = "gpt-35-turbo-16k"
|
|
183
|
+
TEXT_EMBEDDING_ADA_002 = "text-embedding-ada-002"
|
|
184
|
+
TEXT_EMBEDDING_3_SMALL = "text-embedding-3-small"
|
|
185
|
+
TEXT_EMBEDDING_3_LARGE = "text-embedding-3-large"
|
|
186
|
+
DALL_E_3 = "dall-e-3"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class AzureOpenAIClient:
|
|
190
|
+
"""
|
|
191
|
+
Azure OpenAI client with enterprise features
|
|
192
|
+
|
|
193
|
+
Features:
|
|
194
|
+
- Managed identity authentication (preferred)
|
|
195
|
+
- API key authentication (fallback)
|
|
196
|
+
- Key Vault integration
|
|
197
|
+
- Content filtering
|
|
198
|
+
- Cost tracking per deployment
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def __init__(self, config: AzureOpenAIConfig):
|
|
202
|
+
self.config = config
|
|
203
|
+
self.logger = logging.getLogger(__name__)
|
|
204
|
+
|
|
205
|
+
# Setup authentication
|
|
206
|
+
self._setup_authentication()
|
|
207
|
+
|
|
208
|
+
# Initialize clients
|
|
209
|
+
self.client = self._create_sync_client()
|
|
210
|
+
self.async_client = self._create_async_client()
|
|
211
|
+
|
|
212
|
+
# Metrics
|
|
213
|
+
self.metrics = {
|
|
214
|
+
"requests_by_deployment": {},
|
|
215
|
+
"tokens_by_deployment": {},
|
|
216
|
+
"costs_by_deployment": {},
|
|
217
|
+
"content_filter_flags": 0
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
def _setup_authentication(self):
|
|
221
|
+
"""Configure Azure authentication"""
|
|
222
|
+
if self.config.use_managed_identity:
|
|
223
|
+
# Managed Identity (RECOMMENDED for production)
|
|
224
|
+
self.logger.info("Using Azure Managed Identity")
|
|
225
|
+
self.config.credential = DefaultAzureCredential()
|
|
226
|
+
|
|
227
|
+
# Load API key from Key Vault if configured
|
|
228
|
+
if self.config.key_vault_url:
|
|
229
|
+
self._load_key_from_vault()
|
|
230
|
+
else:
|
|
231
|
+
# API Key authentication
|
|
232
|
+
if not self.config.api_key:
|
|
233
|
+
raise ValueError("api_key required when managed identity is disabled")
|
|
234
|
+
self.logger.warning("Using API key authentication (not recommended for production)")
|
|
235
|
+
|
|
236
|
+
def _load_key_from_vault(self):
|
|
237
|
+
"""Load API key from Azure Key Vault"""
|
|
238
|
+
try:
|
|
239
|
+
secret_client = SecretClient(
|
|
240
|
+
vault_url=self.config.key_vault_url,
|
|
241
|
+
credential=self.config.credential
|
|
242
|
+
)
|
|
243
|
+
secret = secret_client.get_secret(self.config.api_key_secret_name)
|
|
244
|
+
self.config.api_key = secret.value
|
|
245
|
+
self.logger.info(f"Loaded API key from Key Vault: {self.config.key_vault_url}")
|
|
246
|
+
except AzureError as e:
|
|
247
|
+
self.logger.error(f"Failed to load key from Key Vault: {e}")
|
|
248
|
+
raise
|
|
249
|
+
|
|
250
|
+
def _create_sync_client(self) -> AzureOpenAI:
|
|
251
|
+
"""Create synchronous Azure OpenAI client"""
|
|
252
|
+
if self.config.use_managed_identity and self.config.credential:
|
|
253
|
+
return AzureOpenAI(
|
|
254
|
+
azure_endpoint=self.config.azure_endpoint,
|
|
255
|
+
api_version=self.config.api_version,
|
|
256
|
+
azure_ad_token_provider=self._get_azure_ad_token,
|
|
257
|
+
max_retries=self.config.max_retries,
|
|
258
|
+
timeout=self.config.timeout
|
|
259
|
+
)
|
|
260
|
+
else:
|
|
261
|
+
return AzureOpenAI(
|
|
262
|
+
api_key=self.config.api_key,
|
|
263
|
+
azure_endpoint=self.config.azure_endpoint,
|
|
264
|
+
api_version=self.config.api_version,
|
|
265
|
+
max_retries=self.config.max_retries,
|
|
266
|
+
timeout=self.config.timeout
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
def _create_async_client(self) -> AsyncAzureOpenAI:
|
|
270
|
+
"""Create asynchronous Azure OpenAI client"""
|
|
271
|
+
if self.config.use_managed_identity and self.config.credential:
|
|
272
|
+
return AsyncAzureOpenAI(
|
|
273
|
+
azure_endpoint=self.config.azure_endpoint,
|
|
274
|
+
api_version=self.config.api_version,
|
|
275
|
+
azure_ad_token_provider=self._get_azure_ad_token,
|
|
276
|
+
max_retries=self.config.max_retries,
|
|
277
|
+
timeout=self.config.timeout
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
return AsyncAzureOpenAI(
|
|
281
|
+
api_key=self.config.api_key,
|
|
282
|
+
azure_endpoint=self.config.azure_endpoint,
|
|
283
|
+
api_version=self.config.api_version,
|
|
284
|
+
max_retries=self.config.max_retries,
|
|
285
|
+
timeout=self.config.timeout
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _get_azure_ad_token(self):
|
|
289
|
+
"""Get Azure AD token for authentication"""
|
|
290
|
+
if self.config.credential:
|
|
291
|
+
token = self.config.credential.get_token(
|
|
292
|
+
"https://cognitiveservices.azure.com/.default"
|
|
293
|
+
)
|
|
294
|
+
return token.token
|
|
295
|
+
return None
|
|
296
|
+
|
|
297
|
+
def set_logging_level(self, level: int = logging.INFO):
|
|
298
|
+
"""Configure logging"""
|
|
299
|
+
logging.basicConfig(
|
|
300
|
+
level=level,
|
|
301
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# Environment setup
|
|
306
|
+
def load_config_from_env() -> AzureOpenAIConfig:
|
|
307
|
+
"""
|
|
308
|
+
Load Azure OpenAI configuration from environment variables
|
|
309
|
+
|
|
310
|
+
Required environment variables:
|
|
311
|
+
- AZURE_OPENAI_ENDPOINT: Your Azure OpenAI resource endpoint
|
|
312
|
+
- AZURE_OPENAI_DEPLOYMENT: Your deployment name
|
|
313
|
+
|
|
314
|
+
Optional:
|
|
315
|
+
- AZURE_OPENAI_API_KEY: API key (if not using managed identity)
|
|
316
|
+
- AZURE_OPENAI_API_VERSION: API version (defaults to latest)
|
|
317
|
+
- AZURE_KEY_VAULT_URL: Key Vault URL for secret management
|
|
318
|
+
- USE_MANAGED_IDENTITY: "true" to use managed identity (default)
|
|
319
|
+
"""
|
|
320
|
+
from dotenv import load_dotenv
|
|
321
|
+
load_dotenv()
|
|
322
|
+
|
|
323
|
+
use_managed_identity = os.getenv("USE_MANAGED_IDENTITY", "true").lower() == "true"
|
|
324
|
+
|
|
325
|
+
return AzureOpenAIConfig(
|
|
326
|
+
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
|
|
327
|
+
azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT", "gpt-4-deployment"),
|
|
328
|
+
api_version=os.getenv("AZURE_OPENAI_API_VERSION", "2024-02-15-preview"),
|
|
329
|
+
api_key=os.getenv("AZURE_OPENAI_API_KEY") if not use_managed_identity else None,
|
|
330
|
+
use_managed_identity=use_managed_identity,
|
|
331
|
+
key_vault_url=os.getenv("AZURE_KEY_VAULT_URL"),
|
|
332
|
+
temperature=float(os.getenv("AZURE_OPENAI_TEMPERATURE", "0.1")),
|
|
333
|
+
max_tokens=int(os.getenv("AZURE_OPENAI_MAX_TOKENS", "0")) or None
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# Example .env file
|
|
338
|
+
"""
|
|
339
|
+
# Azure OpenAI Configuration
|
|
340
|
+
AZURE_OPENAI_ENDPOINT=https://my-openai-resource.openai.azure.com
|
|
341
|
+
AZURE_OPENAI_DEPLOYMENT=gpt-4-deployment
|
|
342
|
+
AZURE_OPENAI_API_VERSION=2024-02-15-preview
|
|
343
|
+
|
|
344
|
+
# Authentication (choose one)
|
|
345
|
+
USE_MANAGED_IDENTITY=true
|
|
346
|
+
# AZURE_OPENAI_API_KEY=your-api-key-here
|
|
347
|
+
|
|
348
|
+
# Optional: Key Vault
|
|
349
|
+
AZURE_KEY_VAULT_URL=https://my-keyvault.vault.azure.net
|
|
350
|
+
|
|
351
|
+
# Optional: Configuration
|
|
352
|
+
AZURE_OPENAI_TEMPERATURE=0.1
|
|
353
|
+
AZURE_OPENAI_MAX_TOKENS=1000
|
|
354
|
+
"""
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Chat Completions with Azure Deployments
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
from openai.types.chat import ChatCompletion
|
|
361
|
+
|
|
362
|
+
class AzureChatManager:
|
|
363
|
+
"""
|
|
364
|
+
Chat completion manager for Azure OpenAI
|
|
365
|
+
|
|
366
|
+
Key differences from OpenAI:
|
|
367
|
+
- Uses deployment names instead of model names
|
|
368
|
+
- Includes content filtering metadata
|
|
369
|
+
- Regional quota management
|
|
370
|
+
"""
|
|
371
|
+
|
|
372
|
+
def __init__(self, client: AzureOpenAIClient):
|
|
373
|
+
self.client = client
|
|
374
|
+
self.conversation_history: Dict[str, List[Dict[str, str]]] = {}
|
|
375
|
+
|
|
376
|
+
def create_completion(self,
|
|
377
|
+
messages: List[Dict[str, str]],
|
|
378
|
+
deployment_name: str = None,
|
|
379
|
+
temperature: float = None,
|
|
380
|
+
max_tokens: int = None,
|
|
381
|
+
**kwargs) -> ChatCompletion:
|
|
382
|
+
"""
|
|
383
|
+
Create chat completion using Azure deployment
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
messages: Chat messages
|
|
387
|
+
deployment_name: Azure deployment name (not model name!)
|
|
388
|
+
temperature: Sampling temperature
|
|
389
|
+
max_tokens: Maximum tokens to generate
|
|
390
|
+
"""
|
|
391
|
+
deployment = deployment_name or self.client.config.azure_deployment
|
|
392
|
+
|
|
393
|
+
try:
|
|
394
|
+
response = self.client.client.chat.completions.create(
|
|
395
|
+
model=deployment, # This is the DEPLOYMENT name in Azure
|
|
396
|
+
messages=messages,
|
|
397
|
+
temperature=temperature or self.client.config.temperature,
|
|
398
|
+
max_tokens=max_tokens or self.client.config.max_tokens,
|
|
399
|
+
**kwargs
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
# Track metrics by deployment
|
|
403
|
+
self._track_deployment_usage(deployment, response)
|
|
404
|
+
|
|
405
|
+
# Check content filtering
|
|
406
|
+
if hasattr(response, 'prompt_filter_results'):
|
|
407
|
+
self._handle_content_filtering(response)
|
|
408
|
+
|
|
409
|
+
self.client.logger.info(
|
|
410
|
+
f"Completion created using deployment '{deployment}': {response.usage}"
|
|
411
|
+
)
|
|
412
|
+
return response
|
|
413
|
+
|
|
414
|
+
except Exception as e:
|
|
415
|
+
self.client.logger.error(f"Error creating completion: {e}")
|
|
416
|
+
raise
|
|
417
|
+
|
|
418
|
+
async def async_completion(self,
|
|
419
|
+
messages: List[Dict[str, str]],
|
|
420
|
+
deployment_name: str = None,
|
|
421
|
+
**kwargs) -> ChatCompletion:
|
|
422
|
+
"""Create async chat completion"""
|
|
423
|
+
deployment = deployment_name or self.client.config.azure_deployment
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
response = await self.client.async_client.chat.completions.create(
|
|
427
|
+
model=deployment,
|
|
428
|
+
messages=messages,
|
|
429
|
+
**kwargs
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
self._track_deployment_usage(deployment, response)
|
|
433
|
+
return response
|
|
434
|
+
|
|
435
|
+
except Exception as e:
|
|
436
|
+
self.client.logger.error(f"Error in async completion: {e}")
|
|
437
|
+
raise
|
|
438
|
+
|
|
439
|
+
def _track_deployment_usage(self, deployment: str, response: ChatCompletion):
|
|
440
|
+
"""Track usage metrics per deployment for cost allocation"""
|
|
441
|
+
metrics = self.client.metrics
|
|
442
|
+
|
|
443
|
+
if deployment not in metrics["requests_by_deployment"]:
|
|
444
|
+
metrics["requests_by_deployment"][deployment] = 0
|
|
445
|
+
metrics["tokens_by_deployment"][deployment] = 0
|
|
446
|
+
metrics["costs_by_deployment"][deployment] = 0.0
|
|
447
|
+
|
|
448
|
+
metrics["requests_by_deployment"][deployment] += 1
|
|
449
|
+
|
|
450
|
+
if response.usage:
|
|
451
|
+
metrics["tokens_by_deployment"][deployment] += response.usage.total_tokens
|
|
452
|
+
|
|
453
|
+
# Estimate cost (update with actual Azure pricing)
|
|
454
|
+
cost = self._estimate_cost(deployment, response.usage)
|
|
455
|
+
metrics["costs_by_deployment"][deployment] += cost
|
|
456
|
+
|
|
457
|
+
def _estimate_cost(self, deployment: str, usage) -> float:
|
|
458
|
+
"""
|
|
459
|
+
Estimate cost based on Azure OpenAI pricing
|
|
460
|
+
|
|
461
|
+
Note: Prices vary by region and commitment tier
|
|
462
|
+
Always verify with Azure pricing calculator
|
|
463
|
+
"""
|
|
464
|
+
# Example pricing (East US, pay-as-you-go)
|
|
465
|
+
pricing = {
|
|
466
|
+
"gpt-4": {"input": 0.00003, "output": 0.00006},
|
|
467
|
+
"gpt-4-32k": {"input": 0.00006, "output": 0.00012},
|
|
468
|
+
"gpt-35-turbo": {"input": 0.0000015, "output": 0.000002},
|
|
469
|
+
"text-embedding-ada-002": {"input": 0.0000001, "output": 0}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
# Map deployment to pricing tier
|
|
473
|
+
model_type = deployment.split("-deployment")[0]
|
|
474
|
+
if model_type in pricing:
|
|
475
|
+
price_info = pricing[model_type]
|
|
476
|
+
input_cost = usage.prompt_tokens * price_info["input"]
|
|
477
|
+
output_cost = usage.completion_tokens * price_info["output"]
|
|
478
|
+
return input_cost + output_cost
|
|
479
|
+
|
|
480
|
+
return 0.0
|
|
481
|
+
|
|
482
|
+
def _handle_content_filtering(self, response: ChatCompletion):
|
|
483
|
+
"""Handle Azure content filtering results"""
|
|
484
|
+
if hasattr(response, 'prompt_filter_results'):
|
|
485
|
+
for result in response.prompt_filter_results:
|
|
486
|
+
if result.get('content_filter_results'):
|
|
487
|
+
self.client.metrics["content_filter_flags"] += 1
|
|
488
|
+
self.client.logger.warning(
|
|
489
|
+
f"Content filtering triggered: {result['content_filter_results']}"
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
def get_deployment_metrics(self, deployment: str = None) -> Dict[str, Any]:
|
|
493
|
+
"""Get usage metrics for specific deployment or all deployments"""
|
|
494
|
+
if deployment:
|
|
495
|
+
return {
|
|
496
|
+
"deployment": deployment,
|
|
497
|
+
"requests": self.client.metrics["requests_by_deployment"].get(deployment, 0),
|
|
498
|
+
"tokens": self.client.metrics["tokens_by_deployment"].get(deployment, 0),
|
|
499
|
+
"estimated_cost": self.client.metrics["costs_by_deployment"].get(deployment, 0.0)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
"all_deployments": self.client.metrics["requests_by_deployment"],
|
|
504
|
+
"total_tokens": sum(self.client.metrics["tokens_by_deployment"].values()),
|
|
505
|
+
"total_cost": sum(self.client.metrics["costs_by_deployment"].values()),
|
|
506
|
+
"content_filter_flags": self.client.metrics["content_filter_flags"]
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
# Usage example
|
|
511
|
+
async def azure_chat_example():
|
|
512
|
+
"""
|
|
513
|
+
Example showing Azure-specific patterns
|
|
514
|
+
"""
|
|
515
|
+
config = load_config_from_env()
|
|
516
|
+
client = AzureOpenAIClient(config)
|
|
517
|
+
chat = AzureChatManager(client)
|
|
518
|
+
|
|
519
|
+
messages = [
|
|
520
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
521
|
+
{"role": "user", "content": "Explain Azure OpenAI Service benefits."}
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
# Use specific deployment
|
|
525
|
+
response = chat.create_completion(
|
|
526
|
+
messages=messages,
|
|
527
|
+
deployment_name="gpt-4-deployment" # Your deployment name
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
print(f"Response: {response.choices[0].message.content}")
|
|
531
|
+
|
|
532
|
+
# Check deployment metrics
|
|
533
|
+
metrics = chat.get_deployment_metrics("gpt-4-deployment")
|
|
534
|
+
print(f"Deployment metrics: {metrics}")
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Enterprise Security Patterns
|
|
538
|
+
|
|
539
|
+
### Managed Identity Integration
|
|
540
|
+
|
|
541
|
+
```python
|
|
542
|
+
from azure.identity import ManagedIdentityCredential, DefaultAzureCredential
|
|
543
|
+
from azure.keyvault.secrets import SecretClient
|
|
544
|
+
|
|
545
|
+
class EnterpriseAzureOpenAIClient:
|
|
546
|
+
"""
|
|
547
|
+
Enterprise-grade Azure OpenAI client with full security features
|
|
548
|
+
|
|
549
|
+
Features:
|
|
550
|
+
- Managed Identity authentication (system or user-assigned)
|
|
551
|
+
- Key Vault integration
|
|
552
|
+
- Private endpoint support
|
|
553
|
+
- Audit logging
|
|
554
|
+
- RBAC enforcement
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
def __init__(
|
|
558
|
+
self,
|
|
559
|
+
azure_endpoint: str,
|
|
560
|
+
deployment_name: str,
|
|
561
|
+
use_system_managed_identity: bool = True,
|
|
562
|
+
user_assigned_identity_client_id: Optional[str] = None,
|
|
563
|
+
key_vault_url: Optional[str] = None,
|
|
564
|
+
enable_audit_logging: bool = True
|
|
565
|
+
):
|
|
566
|
+
self.azure_endpoint = azure_endpoint
|
|
567
|
+
self.deployment_name = deployment_name
|
|
568
|
+
self.enable_audit_logging = enable_audit_logging
|
|
569
|
+
|
|
570
|
+
# Setup credential
|
|
571
|
+
if use_system_managed_identity:
|
|
572
|
+
self.credential = DefaultAzureCredential()
|
|
573
|
+
self.logger.info("Using system-assigned managed identity")
|
|
574
|
+
elif user_assigned_identity_client_id:
|
|
575
|
+
self.credential = ManagedIdentityCredential(
|
|
576
|
+
client_id=user_assigned_identity_client_id
|
|
577
|
+
)
|
|
578
|
+
self.logger.info(f"Using user-assigned managed identity: {user_assigned_identity_client_id}")
|
|
579
|
+
else:
|
|
580
|
+
raise ValueError("Must specify managed identity configuration")
|
|
581
|
+
|
|
582
|
+
# Load secrets from Key Vault if configured
|
|
583
|
+
self.key_vault_client = None
|
|
584
|
+
if key_vault_url:
|
|
585
|
+
self.key_vault_client = SecretClient(
|
|
586
|
+
vault_url=key_vault_url,
|
|
587
|
+
credential=self.credential
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
# Initialize Azure OpenAI client
|
|
591
|
+
self.client = AsyncAzureOpenAI(
|
|
592
|
+
azure_endpoint=self.azure_endpoint,
|
|
593
|
+
api_version="2024-02-15-preview",
|
|
594
|
+
azure_ad_token_provider=self._get_azure_ad_token,
|
|
595
|
+
max_retries=3,
|
|
596
|
+
timeout=60.0
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
# Audit log
|
|
600
|
+
self.audit_log: List[Dict[str, Any]] = []
|
|
601
|
+
|
|
602
|
+
def _get_azure_ad_token(self):
|
|
603
|
+
"""Get Azure AD token using managed identity"""
|
|
604
|
+
token = self.credential.get_token(
|
|
605
|
+
"https://cognitiveservices.azure.com/.default"
|
|
606
|
+
)
|
|
607
|
+
return token.token
|
|
608
|
+
|
|
609
|
+
def get_secret(self, secret_name: str) -> str:
|
|
610
|
+
"""Retrieve secret from Azure Key Vault"""
|
|
611
|
+
if not self.key_vault_client:
|
|
612
|
+
raise ValueError("Key Vault not configured")
|
|
613
|
+
|
|
614
|
+
secret = self.key_vault_client.get_secret(secret_name)
|
|
615
|
+
return secret.value
|
|
616
|
+
|
|
617
|
+
async def create_completion_with_audit(
|
|
618
|
+
self,
|
|
619
|
+
messages: List[Dict[str, str]],
|
|
620
|
+
user_id: str,
|
|
621
|
+
tenant_id: str,
|
|
622
|
+
**kwargs
|
|
623
|
+
) -> ChatCompletion:
|
|
624
|
+
"""
|
|
625
|
+
Create completion with full audit trail
|
|
626
|
+
|
|
627
|
+
Args:
|
|
628
|
+
messages: Chat messages
|
|
629
|
+
user_id: User identifier for audit
|
|
630
|
+
tenant_id: Tenant identifier for multi-tenancy
|
|
631
|
+
"""
|
|
632
|
+
start_time = datetime.utcnow()
|
|
633
|
+
|
|
634
|
+
try:
|
|
635
|
+
response = await self.client.chat.completions.create(
|
|
636
|
+
model=self.deployment_name,
|
|
637
|
+
messages=messages,
|
|
638
|
+
**kwargs
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
# Audit logging
|
|
642
|
+
if self.enable_audit_logging:
|
|
643
|
+
self._log_audit_event(
|
|
644
|
+
user_id=user_id,
|
|
645
|
+
tenant_id=tenant_id,
|
|
646
|
+
deployment=self.deployment_name,
|
|
647
|
+
tokens_used=response.usage.total_tokens if response.usage else 0,
|
|
648
|
+
timestamp=start_time,
|
|
649
|
+
success=True
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
return response
|
|
653
|
+
|
|
654
|
+
except Exception as e:
|
|
655
|
+
# Log failure
|
|
656
|
+
if self.enable_audit_logging:
|
|
657
|
+
self._log_audit_event(
|
|
658
|
+
user_id=user_id,
|
|
659
|
+
tenant_id=tenant_id,
|
|
660
|
+
deployment=self.deployment_name,
|
|
661
|
+
tokens_used=0,
|
|
662
|
+
timestamp=start_time,
|
|
663
|
+
success=False,
|
|
664
|
+
error=str(e)
|
|
665
|
+
)
|
|
666
|
+
raise
|
|
667
|
+
|
|
668
|
+
def _log_audit_event(
|
|
669
|
+
self,
|
|
670
|
+
user_id: str,
|
|
671
|
+
tenant_id: str,
|
|
672
|
+
deployment: str,
|
|
673
|
+
tokens_used: int,
|
|
674
|
+
timestamp: datetime,
|
|
675
|
+
success: bool,
|
|
676
|
+
error: Optional[str] = None
|
|
677
|
+
):
|
|
678
|
+
"""Log audit event for compliance"""
|
|
679
|
+
audit_entry = {
|
|
680
|
+
"timestamp": timestamp.isoformat(),
|
|
681
|
+
"user_id": user_id,
|
|
682
|
+
"tenant_id": tenant_id,
|
|
683
|
+
"deployment": deployment,
|
|
684
|
+
"tokens_used": tokens_used,
|
|
685
|
+
"success": success,
|
|
686
|
+
"error": error
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
self.audit_log.append(audit_entry)
|
|
690
|
+
|
|
691
|
+
# In production: Send to Azure Monitor, Log Analytics, or Event Hub
|
|
692
|
+
self.logger.info(f"Audit event logged: {audit_entry}")
|
|
693
|
+
|
|
694
|
+
def export_audit_log(self, filepath: str):
|
|
695
|
+
"""Export audit log for compliance reporting"""
|
|
696
|
+
import json
|
|
697
|
+
|
|
698
|
+
with open(filepath, 'w') as f:
|
|
699
|
+
json.dump(self.audit_log, f, indent=2)
|
|
700
|
+
|
|
701
|
+
self.logger.info(f"Audit log exported to {filepath}")
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Private Endpoint Configuration
|
|
705
|
+
|
|
706
|
+
```python
|
|
707
|
+
"""
|
|
708
|
+
Azure Private Endpoint Configuration for Azure OpenAI
|
|
709
|
+
|
|
710
|
+
This ensures traffic stays within Azure virtual network.
|
|
711
|
+
|
|
712
|
+
Prerequisites:
|
|
713
|
+
1. Azure OpenAI resource created
|
|
714
|
+
2. Virtual Network with subnet
|
|
715
|
+
3. Private DNS zone configured
|
|
716
|
+
|
|
717
|
+
Steps:
|
|
718
|
+
1. Create private endpoint in Azure Portal or via Azure CLI
|
|
719
|
+
2. Configure DNS resolution
|
|
720
|
+
3. Update application configuration
|
|
721
|
+
"""
|
|
722
|
+
|
|
723
|
+
# Example configuration for applications using private endpoints
|
|
724
|
+
PRIVATE_ENDPOINT_CONFIG = {
|
|
725
|
+
"azure_endpoint": "https://my-openai-resource.openai.azure.com",
|
|
726
|
+
"use_private_endpoint": True,
|
|
727
|
+
"vnet_integration": True,
|
|
728
|
+
"dns_zone": "privatelink.openai.azure.com"
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
# Azure CLI commands for private endpoint setup
|
|
732
|
+
PRIVATE_ENDPOINT_SETUP_COMMANDS = """
|
|
733
|
+
# Create private endpoint
|
|
734
|
+
az network private-endpoint create \\
|
|
735
|
+
--name my-openai-private-endpoint \\
|
|
736
|
+
--resource-group my-resource-group \\
|
|
737
|
+
--vnet-name my-vnet \\
|
|
738
|
+
--subnet my-subnet \\
|
|
739
|
+
--private-connection-resource-id /subscriptions/{subscription-id}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices/accounts/{openai-resource} \\
|
|
740
|
+
--group-id account \\
|
|
741
|
+
--connection-name my-openai-connection
|
|
742
|
+
|
|
743
|
+
# Create private DNS zone
|
|
744
|
+
az network private-dns zone create \\
|
|
745
|
+
--resource-group my-resource-group \\
|
|
746
|
+
--name privatelink.openai.azure.com
|
|
747
|
+
|
|
748
|
+
# Link DNS zone to VNet
|
|
749
|
+
az network private-dns link vnet create \\
|
|
750
|
+
--resource-group my-resource-group \\
|
|
751
|
+
--zone-name privatelink.openai.azure.com \\
|
|
752
|
+
--name my-dns-link \\
|
|
753
|
+
--virtual-network my-vnet \\
|
|
754
|
+
--registration-enabled false
|
|
755
|
+
|
|
756
|
+
# Create DNS zone group
|
|
757
|
+
az network private-endpoint dns-zone-group create \\
|
|
758
|
+
--resource-group my-resource-group \\
|
|
759
|
+
--endpoint-name my-openai-private-endpoint \\
|
|
760
|
+
--name my-zone-group \\
|
|
761
|
+
--private-dns-zone privatelink.openai.azure.com \\
|
|
762
|
+
--zone-name openai
|
|
763
|
+
"""
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
## Multi-Tenant Architecture Patterns
|
|
767
|
+
|
|
768
|
+
```python
|
|
769
|
+
from typing import Dict, List
|
|
770
|
+
import uuid
|
|
771
|
+
|
|
772
|
+
class MultiTenantAzureOpenAI:
|
|
773
|
+
"""
|
|
774
|
+
Multi-tenant Azure OpenAI management
|
|
775
|
+
|
|
776
|
+
Features:
|
|
777
|
+
- Tenant isolation
|
|
778
|
+
- Per-tenant cost tracking
|
|
779
|
+
- Quota management
|
|
780
|
+
- Deployment assignment
|
|
781
|
+
"""
|
|
782
|
+
|
|
783
|
+
def __init__(self):
|
|
784
|
+
self.tenants: Dict[str, Dict[str, Any]] = {}
|
|
785
|
+
self.tenant_clients: Dict[str, AzureOpenAIClient] = {}
|
|
786
|
+
self.logger = logging.getLogger(__name__)
|
|
787
|
+
|
|
788
|
+
def register_tenant(
|
|
789
|
+
self,
|
|
790
|
+
tenant_id: str,
|
|
791
|
+
tenant_name: str,
|
|
792
|
+
azure_endpoint: str,
|
|
793
|
+
deployment_name: str,
|
|
794
|
+
monthly_quota_tokens: int,
|
|
795
|
+
cost_center: str
|
|
796
|
+
):
|
|
797
|
+
"""
|
|
798
|
+
Register a new tenant with isolated resources
|
|
799
|
+
|
|
800
|
+
Args:
|
|
801
|
+
tenant_id: Unique tenant identifier
|
|
802
|
+
tenant_name: Tenant display name
|
|
803
|
+
azure_endpoint: Azure OpenAI endpoint (can be shared or dedicated)
|
|
804
|
+
deployment_name: Deployment assigned to this tenant
|
|
805
|
+
monthly_quota_tokens: Token quota per month
|
|
806
|
+
cost_center: Cost allocation center
|
|
807
|
+
"""
|
|
808
|
+
self.tenants[tenant_id] = {
|
|
809
|
+
"tenant_name": tenant_name,
|
|
810
|
+
"azure_endpoint": azure_endpoint,
|
|
811
|
+
"deployment_name": deployment_name,
|
|
812
|
+
"monthly_quota_tokens": monthly_quota_tokens,
|
|
813
|
+
"cost_center": cost_center,
|
|
814
|
+
"tokens_used_this_month": 0,
|
|
815
|
+
"total_cost": 0.0,
|
|
816
|
+
"created_at": datetime.utcnow().isoformat()
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
# Create dedicated client for tenant
|
|
820
|
+
config = AzureOpenAIConfig(
|
|
821
|
+
azure_endpoint=azure_endpoint,
|
|
822
|
+
azure_deployment=deployment_name,
|
|
823
|
+
use_managed_identity=True
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
self.tenant_clients[tenant_id] = AzureOpenAIClient(config)
|
|
827
|
+
|
|
828
|
+
self.logger.info(f"Registered tenant: {tenant_name} (ID: {tenant_id})")
|
|
829
|
+
|
|
830
|
+
async def create_completion_for_tenant(
|
|
831
|
+
self,
|
|
832
|
+
tenant_id: str,
|
|
833
|
+
messages: List[Dict[str, str]],
|
|
834
|
+
user_id: str,
|
|
835
|
+
**kwargs
|
|
836
|
+
) -> ChatCompletion:
|
|
837
|
+
"""
|
|
838
|
+
Create completion for specific tenant with quota enforcement
|
|
839
|
+
|
|
840
|
+
Args:
|
|
841
|
+
tenant_id: Tenant identifier
|
|
842
|
+
messages: Chat messages
|
|
843
|
+
user_id: User identifier within tenant
|
|
844
|
+
"""
|
|
845
|
+
if tenant_id not in self.tenants:
|
|
846
|
+
raise ValueError(f"Tenant not found: {tenant_id}")
|
|
847
|
+
|
|
848
|
+
tenant = self.tenants[tenant_id]
|
|
849
|
+
|
|
850
|
+
# Check quota
|
|
851
|
+
if tenant["tokens_used_this_month"] >= tenant["monthly_quota_tokens"]:
|
|
852
|
+
raise Exception(
|
|
853
|
+
f"Tenant {tenant['tenant_name']} has exceeded monthly quota. "
|
|
854
|
+
f"Used: {tenant['tokens_used_this_month']}, "
|
|
855
|
+
f"Quota: {tenant['monthly_quota_tokens']}"
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
# Get tenant's client
|
|
859
|
+
client = self.tenant_clients[tenant_id]
|
|
860
|
+
chat_manager = AzureChatManager(client)
|
|
861
|
+
|
|
862
|
+
# Create completion
|
|
863
|
+
response = await chat_manager.async_completion(
|
|
864
|
+
messages=messages,
|
|
865
|
+
deployment_name=tenant["deployment_name"],
|
|
866
|
+
**kwargs
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
# Update tenant usage
|
|
870
|
+
if response.usage:
|
|
871
|
+
tenant["tokens_used_this_month"] += response.usage.total_tokens
|
|
872
|
+
|
|
873
|
+
# Calculate cost
|
|
874
|
+
cost = chat_manager._estimate_cost(
|
|
875
|
+
tenant["deployment_name"],
|
|
876
|
+
response.usage
|
|
877
|
+
)
|
|
878
|
+
tenant["total_cost"] += cost
|
|
879
|
+
|
|
880
|
+
self.logger.info(
|
|
881
|
+
f"Tenant {tenant['tenant_name']}: "
|
|
882
|
+
f"Used {response.usage.total_tokens} tokens, "
|
|
883
|
+
f"Cost: ${cost:.4f}, "
|
|
884
|
+
f"Monthly usage: {tenant['tokens_used_this_month']}/{tenant['monthly_quota_tokens']}"
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
return response
|
|
888
|
+
|
|
889
|
+
def get_tenant_usage_report(self, tenant_id: str) -> Dict[str, Any]:
|
|
890
|
+
"""Generate usage report for tenant"""
|
|
891
|
+
if tenant_id not in self.tenants:
|
|
892
|
+
raise ValueError(f"Tenant not found: {tenant_id}")
|
|
893
|
+
|
|
894
|
+
tenant = self.tenants[tenant_id]
|
|
895
|
+
|
|
896
|
+
return {
|
|
897
|
+
"tenant_id": tenant_id,
|
|
898
|
+
"tenant_name": tenant["tenant_name"],
|
|
899
|
+
"cost_center": tenant["cost_center"],
|
|
900
|
+
"tokens_used": tenant["tokens_used_this_month"],
|
|
901
|
+
"quota": tenant["monthly_quota_tokens"],
|
|
902
|
+
"utilization_percent": (
|
|
903
|
+
tenant["tokens_used_this_month"] / tenant["monthly_quota_tokens"] * 100
|
|
904
|
+
),
|
|
905
|
+
"total_cost": tenant["total_cost"],
|
|
906
|
+
"deployment": tenant["deployment_name"]
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
def export_chargeback_report(self, filepath: str):
|
|
910
|
+
"""Export cost allocation report for all tenants"""
|
|
911
|
+
import json
|
|
912
|
+
|
|
913
|
+
report = {
|
|
914
|
+
"report_generated": datetime.utcnow().isoformat(),
|
|
915
|
+
"tenants": [
|
|
916
|
+
self.get_tenant_usage_report(tenant_id)
|
|
917
|
+
for tenant_id in self.tenants.keys()
|
|
918
|
+
],
|
|
919
|
+
"total_cost": sum(t["total_cost"] for t in self.tenants.values())
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
with open(filepath, 'w') as f:
|
|
923
|
+
json.dump(report, f, indent=2)
|
|
924
|
+
|
|
925
|
+
self.logger.info(f"Chargeback report exported to {filepath}")
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
# Usage example
|
|
929
|
+
async def multi_tenant_example():
|
|
930
|
+
"""
|
|
931
|
+
Example: Multi-tenant SaaS application with Azure OpenAI
|
|
932
|
+
"""
|
|
933
|
+
mt_manager = MultiTenantAzureOpenAI()
|
|
934
|
+
|
|
935
|
+
# Register tenants
|
|
936
|
+
mt_manager.register_tenant(
|
|
937
|
+
tenant_id="tenant-123",
|
|
938
|
+
tenant_name="Acme Corporation",
|
|
939
|
+
azure_endpoint="https://my-openai-eastus.openai.azure.com",
|
|
940
|
+
deployment_name="gpt-4-acme",
|
|
941
|
+
monthly_quota_tokens=1000000,
|
|
942
|
+
cost_center="CC-ACME-001"
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
mt_manager.register_tenant(
|
|
946
|
+
tenant_id="tenant-456",
|
|
947
|
+
tenant_name="TechStart Inc",
|
|
948
|
+
azure_endpoint="https://my-openai-westus.openai.azure.com",
|
|
949
|
+
deployment_name="gpt-35-turbo-techstart",
|
|
950
|
+
monthly_quota_tokens=500000,
|
|
951
|
+
cost_center="CC-TECH-002"
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
# Create completion for tenant
|
|
955
|
+
messages = [
|
|
956
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
957
|
+
{"role": "user", "content": "Explain multi-tenancy benefits."}
|
|
958
|
+
]
|
|
959
|
+
|
|
960
|
+
response = await mt_manager.create_completion_for_tenant(
|
|
961
|
+
tenant_id="tenant-123",
|
|
962
|
+
messages=messages,
|
|
963
|
+
user_id="user-789"
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
print(f"Response: {response.choices[0].message.content}")
|
|
967
|
+
|
|
968
|
+
# Get usage report
|
|
969
|
+
report = mt_manager.get_tenant_usage_report("tenant-123")
|
|
970
|
+
print(f"Usage report: {report}")
|
|
971
|
+
|
|
972
|
+
# Export chargeback
|
|
973
|
+
mt_manager.export_chargeback_report("chargeback_report.json")
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
## Azure-Specific Features
|
|
977
|
+
|
|
978
|
+
### Content Filtering Configuration
|
|
979
|
+
|
|
980
|
+
```python
|
|
981
|
+
"""
|
|
982
|
+
Azure OpenAI Content Filtering
|
|
983
|
+
|
|
984
|
+
Azure provides built-in content filtering for:
|
|
985
|
+
- Hate speech
|
|
986
|
+
- Sexual content
|
|
987
|
+
- Violence
|
|
988
|
+
- Self-harm
|
|
989
|
+
|
|
990
|
+
Severity levels: safe, low, medium, high
|
|
991
|
+
|
|
992
|
+
Configuration is done in Azure Portal per deployment.
|
|
993
|
+
"""
|
|
994
|
+
|
|
995
|
+
def handle_content_filter_response(response: ChatCompletion):
|
|
996
|
+
"""
|
|
997
|
+
Handle content filtering results from Azure OpenAI
|
|
998
|
+
|
|
999
|
+
Azure includes filtering metadata in responses.
|
|
1000
|
+
"""
|
|
1001
|
+
# Check prompt filtering
|
|
1002
|
+
if hasattr(response, 'prompt_filter_results'):
|
|
1003
|
+
for idx, result in enumerate(response.prompt_filter_results):
|
|
1004
|
+
filters = result.get('content_filter_results', {})
|
|
1005
|
+
|
|
1006
|
+
for category, details in filters.items():
|
|
1007
|
+
severity = details.get('severity')
|
|
1008
|
+
filtered = details.get('filtered', False)
|
|
1009
|
+
|
|
1010
|
+
if filtered:
|
|
1011
|
+
print(f"Prompt {idx} filtered for {category}: {severity}")
|
|
1012
|
+
# Handle filtered content (reject request, sanitize, etc.)
|
|
1013
|
+
raise ValueError(f"Content filtered: {category} ({severity})")
|
|
1014
|
+
|
|
1015
|
+
# Check output filtering
|
|
1016
|
+
if hasattr(response, 'choices'):
|
|
1017
|
+
for choice in response.choices:
|
|
1018
|
+
if hasattr(choice, 'content_filter_results'):
|
|
1019
|
+
filters = choice.content_filter_results or {}
|
|
1020
|
+
|
|
1021
|
+
for category, details in filters.items():
|
|
1022
|
+
severity = details.get('severity')
|
|
1023
|
+
filtered = details.get('filtered', False)
|
|
1024
|
+
|
|
1025
|
+
if filtered:
|
|
1026
|
+
print(f"Output filtered for {category}: {severity}")
|
|
1027
|
+
# Handle filtered output
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
# Example with content filtering
|
|
1031
|
+
async def content_filtering_example():
|
|
1032
|
+
config = load_config_from_env()
|
|
1033
|
+
client = AzureOpenAIClient(config)
|
|
1034
|
+
chat = AzureChatManager(client)
|
|
1035
|
+
|
|
1036
|
+
messages = [
|
|
1037
|
+
{"role": "user", "content": "Tell me about safety features."}
|
|
1038
|
+
]
|
|
1039
|
+
|
|
1040
|
+
try:
|
|
1041
|
+
response = chat.create_completion(messages)
|
|
1042
|
+
handle_content_filter_response(response)
|
|
1043
|
+
print(response.choices[0].message.content)
|
|
1044
|
+
except ValueError as e:
|
|
1045
|
+
print(f"Content filtering error: {e}")
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
### Regional Deployment and Failover
|
|
1049
|
+
|
|
1050
|
+
```python
|
|
1051
|
+
from typing import List
|
|
1052
|
+
|
|
1053
|
+
class RegionalAzureOpenAI:
|
|
1054
|
+
"""
|
|
1055
|
+
Regional deployment with automatic failover
|
|
1056
|
+
|
|
1057
|
+
Features:
|
|
1058
|
+
- Primary and secondary regions
|
|
1059
|
+
- Automatic failover on errors
|
|
1060
|
+
- Load balancing across regions
|
|
1061
|
+
- Regional quota management
|
|
1062
|
+
"""
|
|
1063
|
+
|
|
1064
|
+
def __init__(self, regional_configs: List[Dict[str, Any]]):
|
|
1065
|
+
"""
|
|
1066
|
+
Initialize with multiple regional endpoints
|
|
1067
|
+
|
|
1068
|
+
Args:
|
|
1069
|
+
regional_configs: List of region configurations
|
|
1070
|
+
[
|
|
1071
|
+
{
|
|
1072
|
+
"region": "eastus",
|
|
1073
|
+
"endpoint": "https://...",
|
|
1074
|
+
"deployment": "gpt-4-eastus",
|
|
1075
|
+
"priority": 1 # Lower = higher priority
|
|
1076
|
+
},
|
|
1077
|
+
...
|
|
1078
|
+
]
|
|
1079
|
+
"""
|
|
1080
|
+
# Sort by priority
|
|
1081
|
+
self.regions = sorted(regional_configs, key=lambda x: x.get("priority", 999))
|
|
1082
|
+
|
|
1083
|
+
# Create clients for each region
|
|
1084
|
+
self.clients = {}
|
|
1085
|
+
for region_config in self.regions:
|
|
1086
|
+
config = AzureOpenAIConfig(
|
|
1087
|
+
azure_endpoint=region_config["endpoint"],
|
|
1088
|
+
azure_deployment=region_config["deployment"],
|
|
1089
|
+
use_managed_identity=True
|
|
1090
|
+
)
|
|
1091
|
+
self.clients[region_config["region"]] = AzureOpenAIClient(config)
|
|
1092
|
+
|
|
1093
|
+
self.logger = logging.getLogger(__name__)
|
|
1094
|
+
|
|
1095
|
+
async def create_completion_with_failover(
|
|
1096
|
+
self,
|
|
1097
|
+
messages: List[Dict[str, str]],
|
|
1098
|
+
**kwargs
|
|
1099
|
+
) -> ChatCompletion:
|
|
1100
|
+
"""
|
|
1101
|
+
Create completion with automatic regional failover
|
|
1102
|
+
|
|
1103
|
+
Tries regions in priority order until successful.
|
|
1104
|
+
"""
|
|
1105
|
+
last_error = None
|
|
1106
|
+
|
|
1107
|
+
for region_config in self.regions:
|
|
1108
|
+
region = region_config["region"]
|
|
1109
|
+
client = self.clients[region]
|
|
1110
|
+
|
|
1111
|
+
try:
|
|
1112
|
+
self.logger.info(f"Attempting completion in region: {region}")
|
|
1113
|
+
|
|
1114
|
+
chat = AzureChatManager(client)
|
|
1115
|
+
response = await chat.async_completion(
|
|
1116
|
+
messages=messages,
|
|
1117
|
+
deployment_name=region_config["deployment"],
|
|
1118
|
+
**kwargs
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
self.logger.info(f"Completion successful in region: {region}")
|
|
1122
|
+
return response
|
|
1123
|
+
|
|
1124
|
+
except Exception as e:
|
|
1125
|
+
self.logger.warning(f"Region {region} failed: {e}")
|
|
1126
|
+
last_error = e
|
|
1127
|
+
continue
|
|
1128
|
+
|
|
1129
|
+
# All regions failed
|
|
1130
|
+
raise Exception(f"All regions failed. Last error: {last_error}")
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
# Usage example
|
|
1134
|
+
async def regional_failover_example():
|
|
1135
|
+
"""
|
|
1136
|
+
Example: Multi-region deployment with failover
|
|
1137
|
+
"""
|
|
1138
|
+
regional_configs = [
|
|
1139
|
+
{
|
|
1140
|
+
"region": "eastus",
|
|
1141
|
+
"endpoint": "https://my-openai-eastus.openai.azure.com",
|
|
1142
|
+
"deployment": "gpt-4-eastus",
|
|
1143
|
+
"priority": 1
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
"region": "westus",
|
|
1147
|
+
"endpoint": "https://my-openai-westus.openai.azure.com",
|
|
1148
|
+
"deployment": "gpt-4-westus",
|
|
1149
|
+
"priority": 2
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
"region": "northeurope",
|
|
1153
|
+
"endpoint": "https://my-openai-northeurope.openai.azure.com",
|
|
1154
|
+
"deployment": "gpt-4-europe",
|
|
1155
|
+
"priority": 3
|
|
1156
|
+
}
|
|
1157
|
+
]
|
|
1158
|
+
|
|
1159
|
+
regional_client = RegionalAzureOpenAI(regional_configs)
|
|
1160
|
+
|
|
1161
|
+
messages = [
|
|
1162
|
+
{"role": "user", "content": "What is regional redundancy?"}
|
|
1163
|
+
]
|
|
1164
|
+
|
|
1165
|
+
response = await regional_client.create_completion_with_failover(messages)
|
|
1166
|
+
print(f"Response: {response.choices[0].message.content}")
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
## Azure Monitor Integration
|
|
1170
|
+
|
|
1171
|
+
```python
|
|
1172
|
+
from azure.monitor.opentelemetry import configure_azure_monitor
|
|
1173
|
+
from opentelemetry import trace
|
|
1174
|
+
from opentelemetry.trace import Status, StatusCode
|
|
1175
|
+
|
|
1176
|
+
class MonitoredAzureOpenAI:
|
|
1177
|
+
"""
|
|
1178
|
+
Azure OpenAI client with Application Insights integration
|
|
1179
|
+
|
|
1180
|
+
Features:
|
|
1181
|
+
- Distributed tracing
|
|
1182
|
+
- Performance metrics
|
|
1183
|
+
- Error tracking
|
|
1184
|
+
- Custom events
|
|
1185
|
+
"""
|
|
1186
|
+
|
|
1187
|
+
def __init__(self, client: AzureOpenAIClient, app_insights_connection_string: str):
|
|
1188
|
+
self.client = client
|
|
1189
|
+
|
|
1190
|
+
# Configure Azure Monitor
|
|
1191
|
+
configure_azure_monitor(connection_string=app_insights_connection_string)
|
|
1192
|
+
|
|
1193
|
+
# Get tracer
|
|
1194
|
+
self.tracer = trace.get_tracer(__name__)
|
|
1195
|
+
|
|
1196
|
+
async def create_completion_with_monitoring(
|
|
1197
|
+
self,
|
|
1198
|
+
messages: List[Dict[str, str]],
|
|
1199
|
+
deployment_name: str,
|
|
1200
|
+
**kwargs
|
|
1201
|
+
) -> ChatCompletion:
|
|
1202
|
+
"""
|
|
1203
|
+
Create completion with full Azure Monitor telemetry
|
|
1204
|
+
"""
|
|
1205
|
+
with self.tracer.start_as_current_span("azure_openai_completion") as span:
|
|
1206
|
+
# Add attributes
|
|
1207
|
+
span.set_attribute("deployment", deployment_name)
|
|
1208
|
+
span.set_attribute("message_count", len(messages))
|
|
1209
|
+
|
|
1210
|
+
try:
|
|
1211
|
+
chat = AzureChatManager(self.client)
|
|
1212
|
+
response = await chat.async_completion(
|
|
1213
|
+
messages=messages,
|
|
1214
|
+
deployment_name=deployment_name,
|
|
1215
|
+
**kwargs
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
# Record metrics
|
|
1219
|
+
if response.usage:
|
|
1220
|
+
span.set_attribute("tokens_total", response.usage.total_tokens)
|
|
1221
|
+
span.set_attribute("tokens_prompt", response.usage.prompt_tokens)
|
|
1222
|
+
span.set_attribute("tokens_completion", response.usage.completion_tokens)
|
|
1223
|
+
|
|
1224
|
+
span.set_status(Status(StatusCode.OK))
|
|
1225
|
+
return response
|
|
1226
|
+
|
|
1227
|
+
except Exception as e:
|
|
1228
|
+
span.set_status(Status(StatusCode.ERROR), str(e))
|
|
1229
|
+
span.record_exception(e)
|
|
1230
|
+
raise
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
# Usage example
|
|
1234
|
+
async def monitoring_example():
|
|
1235
|
+
"""
|
|
1236
|
+
Example: Azure OpenAI with Application Insights
|
|
1237
|
+
"""
|
|
1238
|
+
config = load_config_from_env()
|
|
1239
|
+
client = AzureOpenAIClient(config)
|
|
1240
|
+
|
|
1241
|
+
app_insights_connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING")
|
|
1242
|
+
monitored_client = MonitoredAzureOpenAI(client, app_insights_connection_string)
|
|
1243
|
+
|
|
1244
|
+
messages = [
|
|
1245
|
+
{"role": "user", "content": "Explain Azure monitoring."}
|
|
1246
|
+
]
|
|
1247
|
+
|
|
1248
|
+
response = await monitored_client.create_completion_with_monitoring(
|
|
1249
|
+
messages=messages,
|
|
1250
|
+
deployment_name="gpt-4-deployment"
|
|
1251
|
+
)
|
|
1252
|
+
|
|
1253
|
+
print(f"Response: {response.choices[0].message.content}")
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
## Cost Optimization Strategies
|
|
1257
|
+
|
|
1258
|
+
```python
|
|
1259
|
+
"""
|
|
1260
|
+
Azure OpenAI Cost Optimization Patterns
|
|
1261
|
+
|
|
1262
|
+
1. Provisioned Throughput Units (PTUs)
|
|
1263
|
+
- Fixed monthly cost
|
|
1264
|
+
- Predictable pricing
|
|
1265
|
+
- Best for high-volume workloads
|
|
1266
|
+
|
|
1267
|
+
2. Regional pricing differences
|
|
1268
|
+
- East US: Lower cost
|
|
1269
|
+
- West Europe: Higher cost
|
|
1270
|
+
- Check Azure pricing calculator
|
|
1271
|
+
|
|
1272
|
+
3. Deployment capacity planning
|
|
1273
|
+
- Start with smaller deployments
|
|
1274
|
+
- Scale based on actual usage
|
|
1275
|
+
- Monitor quota utilization
|
|
1276
|
+
|
|
1277
|
+
4. Response caching
|
|
1278
|
+
- Cache similar queries
|
|
1279
|
+
- Reduce API calls
|
|
1280
|
+
- Use Azure Cache for Redis
|
|
1281
|
+
|
|
1282
|
+
5. Quota management
|
|
1283
|
+
- Set per-tenant quotas
|
|
1284
|
+
- Implement rate limiting
|
|
1285
|
+
- Monitor usage patterns
|
|
1286
|
+
"""
|
|
1287
|
+
|
|
1288
|
+
class CostOptimizedAzureOpenAI:
|
|
1289
|
+
"""
|
|
1290
|
+
Cost-optimized Azure OpenAI client
|
|
1291
|
+
|
|
1292
|
+
Features:
|
|
1293
|
+
- Response caching
|
|
1294
|
+
- Request batching
|
|
1295
|
+
- Quota enforcement
|
|
1296
|
+
- Cost alerts
|
|
1297
|
+
"""
|
|
1298
|
+
|
|
1299
|
+
def __init__(
|
|
1300
|
+
self,
|
|
1301
|
+
client: AzureOpenAIClient,
|
|
1302
|
+
cache_ttl_seconds: int = 3600,
|
|
1303
|
+
monthly_budget_usd: float = 1000.0
|
|
1304
|
+
):
|
|
1305
|
+
self.client = client
|
|
1306
|
+
self.cache_ttl = cache_ttl_seconds
|
|
1307
|
+
self.monthly_budget = monthly_budget_usd
|
|
1308
|
+
self.monthly_spent = 0.0
|
|
1309
|
+
|
|
1310
|
+
# Simple in-memory cache (use Redis in production)
|
|
1311
|
+
self.cache: Dict[str, Dict[str, Any]] = {}
|
|
1312
|
+
|
|
1313
|
+
def _get_cache_key(self, messages: List[Dict[str, str]]) -> str:
|
|
1314
|
+
"""Generate cache key from messages"""
|
|
1315
|
+
import hashlib
|
|
1316
|
+
import json
|
|
1317
|
+
|
|
1318
|
+
content = json.dumps(messages, sort_keys=True)
|
|
1319
|
+
return hashlib.sha256(content.encode()).hexdigest()
|
|
1320
|
+
|
|
1321
|
+
async def create_completion_with_caching(
|
|
1322
|
+
self,
|
|
1323
|
+
messages: List[Dict[str, str]],
|
|
1324
|
+
deployment_name: str,
|
|
1325
|
+
**kwargs
|
|
1326
|
+
) -> ChatCompletion:
|
|
1327
|
+
"""
|
|
1328
|
+
Create completion with response caching
|
|
1329
|
+
"""
|
|
1330
|
+
# Check budget
|
|
1331
|
+
if self.monthly_spent >= self.monthly_budget:
|
|
1332
|
+
raise Exception(
|
|
1333
|
+
f"Monthly budget exceeded: ${self.monthly_spent:.2f} / ${self.monthly_budget:.2f}"
|
|
1334
|
+
)
|
|
1335
|
+
|
|
1336
|
+
# Check cache
|
|
1337
|
+
cache_key = self._get_cache_key(messages)
|
|
1338
|
+
|
|
1339
|
+
if cache_key in self.cache:
|
|
1340
|
+
cached_data = self.cache[cache_key]
|
|
1341
|
+
|
|
1342
|
+
# Check if cache is still valid
|
|
1343
|
+
age = (datetime.utcnow() - cached_data["timestamp"]).total_seconds()
|
|
1344
|
+
if age < self.cache_ttl:
|
|
1345
|
+
self.client.logger.info(f"Cache hit: {cache_key[:16]}")
|
|
1346
|
+
return cached_data["response"]
|
|
1347
|
+
|
|
1348
|
+
# Cache miss - create completion
|
|
1349
|
+
chat = AzureChatManager(self.client)
|
|
1350
|
+
response = await chat.async_completion(
|
|
1351
|
+
messages=messages,
|
|
1352
|
+
deployment_name=deployment_name,
|
|
1353
|
+
**kwargs
|
|
1354
|
+
)
|
|
1355
|
+
|
|
1356
|
+
# Update cost tracking
|
|
1357
|
+
if response.usage:
|
|
1358
|
+
cost = chat._estimate_cost(deployment_name, response.usage)
|
|
1359
|
+
self.monthly_spent += cost
|
|
1360
|
+
|
|
1361
|
+
# Cache response
|
|
1362
|
+
self.cache[cache_key] = {
|
|
1363
|
+
"response": response,
|
|
1364
|
+
"timestamp": datetime.utcnow()
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
self.client.logger.info(
|
|
1368
|
+
f"Cache miss: {cache_key[:16]}. "
|
|
1369
|
+
f"Monthly spent: ${self.monthly_spent:.2f} / ${self.monthly_budget:.2f}"
|
|
1370
|
+
)
|
|
1371
|
+
|
|
1372
|
+
return response
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
## Self-Verification Protocol
|
|
1376
|
+
|
|
1377
|
+
Before delivering any Azure OpenAI solution, verify:
|
|
1378
|
+
- [ ] Documentation from Context7 has been consulted
|
|
1379
|
+
- [ ] Using deployment names (not model names)
|
|
1380
|
+
- [ ] API version is explicitly specified
|
|
1381
|
+
- [ ] Managed identity authentication configured (preferred)
|
|
1382
|
+
- [ ] Key Vault integration for secrets (if using API keys)
|
|
1383
|
+
- [ ] Content filtering is enabled and handled
|
|
1384
|
+
- [ ] Regional endpoints are correct
|
|
1385
|
+
- [ ] Cost tracking and quota management implemented
|
|
1386
|
+
- [ ] Audit logging configured for compliance
|
|
1387
|
+
- [ ] Private endpoints configured (if required)
|
|
1388
|
+
- [ ] Multi-tenant isolation enforced (if applicable)
|
|
1389
|
+
- [ ] Error handling includes Azure-specific errors
|
|
1390
|
+
- [ ] Monitoring with Azure Monitor/Application Insights
|
|
1391
|
+
- [ ] Tests are written and passing
|
|
1392
|
+
- [ ] Performance is acceptable
|
|
1393
|
+
- [ ] No resource leaks
|
|
1394
|
+
- [ ] Security best practices followed
|
|
1395
|
+
|
|
1396
|
+
## Azure OpenAI vs OpenAI API Quick Reference
|
|
1397
|
+
|
|
1398
|
+
| Feature | OpenAI API | Azure OpenAI |
|
|
1399
|
+
|---------|-----------|--------------|
|
|
1400
|
+
| **Model Selection** | `model="gpt-4"` | `model="deployment-name"` |
|
|
1401
|
+
| **API Version** | Not required | `api_version="2024-02-15-preview"` |
|
|
1402
|
+
| **Endpoint** | `https://api.openai.com/v1` | `https://{resource}.openai.azure.com` |
|
|
1403
|
+
| **Authentication** | API key only | API key OR Managed Identity (preferred) |
|
|
1404
|
+
| **Content Filtering** | No | Yes (configurable) |
|
|
1405
|
+
| **Data Residency** | US only | Multiple Azure regions |
|
|
1406
|
+
| **Private Endpoints** | No | Yes (VNet integration) |
|
|
1407
|
+
| **Enterprise Support** | Limited | Full Azure support |
|
|
1408
|
+
| **Compliance** | Limited | SOC 2, HIPAA, ISO, etc. |
|
|
1409
|
+
| **Cost Model** | Pay-per-token | Pay-per-token OR PTU |
|
|
1410
|
+
|
|
1411
|
+
You deliver enterprise-grade Azure OpenAI solutions that are secure, compliant, cost-effective, and follow Microsoft Azure best practices for AI deployments.
|