Subchapter 25.29
references/phases/discover/discover-app-code.mdMarkdown18 KBView on GitHub
Fragment unit. Scans source code, detects Azure/AI SDK imports and agentic frameworks, and — when AI confidence ≥ 70% — contributes . The assembler () is its single creator; this fragment produces the content. See for how it composes into the phase. The Azure port of gcp-to-aws’s : the agentic detection ports unchanged; the AI-SDK signals are swapped from GCP/Vertex to Azure.
ai-workload-profile.jsondiscover-assemble.mddiscover.mddiscover-app-code.mdExecute ALL steps in order. If no source code is found, exit cleanly with no output — other Discover fragments (IaC) may still produce artifacts.
Recursively scan for source and dependency manifests: **/*.py, **/*.js, **/*.ts, **/*.jsx,
**/*.tsx, **/*.go, **/*.java, **/*.cs (.NET is common on Azure); manifests
requirements.txt, pyproject.toml, Pipfile, package.json, go.mod, pom.xml,
build.gradle, *.csproj. Exit gate: no source or manifest → exit cleanly, no artifact.
Secret-file exclusion (HARD). Before reading any file, skip entirely: .env*,
*.pem/*.key/*.p12/*.pfx, credentials.json, *-credentials.json, secrets.yaml/.yml,
azure.json service-principal files. Log a skip line; never include contents. (Azure test repos
ship .env.example with placeholder values — still skipped.)
Recognized but excluded (no AWS recommendation): Auth0, Supabase Auth, Firebase Auth, Clerk, Okta,
Keycloak, NextAuth, and Microsoft Entra ID auth libraries used purely for sign-in (msal,
@azure/msal-*, azure-identity when used ONLY for DefaultAzureCredential token acquisition to
auth OTHER Azure calls). Log and skip; identity is handled by Clarify Category J, not here.
Scan for Azure SDK and AI-provider imports. Record file_path, import_statement,
inferred_service, confidence (0.60–0.80 — inferred from code, not config).
| Import | Inferred service |
|---|---|
azure.storage.blob / @azure/storage-blob | Blob Storage |
azure.cosmos / @azure/cosmos | Cosmos DB |
azure.servicebus / @azure/service-bus | Service Bus |
azure.keyvault.* / @azure/keyvault-* | Key Vault |
redis / ioredis | Azure Cache for Redis |
azure.identity (for data-plane calls) | managed identity |
The AI-relevant imports are the point of this fragment — see Step 3.
Map the Step-1 imports to likely Azure resources at 0.60–0.80 confidence (inferring existence, not reading config). These supplement IaC evidence; the assembler reconciles.
Scan active (non-commented) code for websocket, WebSocket, socket.io, FastAPI WebSocket,
ws package, EventSource, text/event-stream. Record websocket_signals_found +
websocket_signal_files in discovery metadata. If no source was found, do NOT set the flag
(absence of a scan is not evidence of no WebSockets).
Scan source and manifests for AI patterns. Record pattern, file location, confidence.
| Pattern | What to look for | Confidence |
|---|---|---|
| 3.1 Azure OpenAI (Python) | from openai import AzureOpenAI / AsyncAzureOpenAI; a client built with azure_endpoint=, api_version=; openai.api_type = "azure" (legacy); env AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_API_KEY / AZURE_OPENAI_DEPLOYMENT_NAME | 98% |
| 3.2 Azure OpenAI (Node) | @azure/openai, AzureOpenAI from openai with azureADTokenProvider/endpoint; azure-ai-inference / @azure-rest/ai-inference | 98% |
| 3.3 Azure OpenAI (.NET) | Azure.AI.OpenAI (OpenAIClient with an Azure endpoint) | 95% |
| 3.4 OpenAI direct | from openai import OpenAI / client.chat.completions.create() with NO azure config; model strings gpt-4o, gpt-4.1, o3, o4-mini | 98% |
| 3.5 Anthropic | anthropic, claude-* model strings | 98% |
| 3.6 Azure AI Vision | azure.ai.vision.*, azure-cognitiveservices-vision-computervision, ComputerVisionClient | 90% |
| 3.7 Azure AI Document Intelligence | azure.ai.formrecognizer / azure.ai.documentintelligence, DocumentAnalysisClient, DocumentIntelligenceClient | 90% |
| 3.8 Azure AI Speech | azure.cognitiveservices.speech, SpeechConfig, SpeechRecognizer, SpeechSynthesizer | 90% |
| 3.9 Azure AI Language | azure.ai.textanalytics, TextAnalyticsClient | 90% |
| 3.10 Azure AI Translator | azure.ai.translation.* | 90% |
| 3.11 Azure Machine Learning | azureml, azure.ai.ml, MLClient | 85% |
| 3.12 Azure AI Search (RAG) | azure.search.documents, SearchClient, VectorizedQuery — with embeddings, a RAG signal | 85% |
| 3.13 Embeddings & RAG | langchain + AzureOpenAIEmbeddings; llama_index + Azure; vector DB + embeddings | 85% |
Manifest deps to check: openai, azure-ai-inference, @azure/openai, Azure.AI.OpenAI,
azure-ai-formrecognizer, azure-ai-documentintelligence, azure-cognitiveservices-vision-computervision,
azure-cognitiveservices-speech, azure-ai-textanalytics, azure-ai-translation-text, azureml,
azure-ai-ml, azure-search-documents, anthropic, litellm, langchain, langchain-openai,
langchain-aws.
| Pattern | What to look for | Confidence |
|---|---|---|
| 3B.1 LangGraph | from langgraph, StateGraph(, add_node(, add_edge(, .compile() | 95% |
| 3B.2 CrewAI | from crewai, Crew(, Agent( with role=, Task( | 95% |
| 3B.3 AutoGen | from autogen, AssistantAgent(, GroupChat(, ConversableAgent( | 95% |
| 3B.4 OpenAI Agents SDK | from openai.agents / from agents import, openai.beta.assistants, Runner( | 95% |
| 3B.5 Strands | from strands, Agent( with tools=, Swarm(, GraphBuilder( | 95% |
| 3B.5a Pydantic AI | from pydantic_ai import Agent, Agent(model=, .run_sync( | 95% |
| 3B.5b Agno | from agno.agent import Agent, Team(, .print_response( | 95% |
| 3B.6 Custom agent loop | a loop with BOTH an LLM call AND tool dispatch from model output AND result parse-back | 80% |
| 3B.7 Tool definitions | @tool decorators, tools=[{...schema...}], function_declarations= | 90% |
| 3B.8 MCP integration | from mcp.server / from mcp.client, mcp.json, MCPClient( | 90% |
| 3B.9 Agent memory | ConversationBufferMemory(, ChatMessageHistory(, MemorySaver(, vector-store retrieval into agent context | 85% |
Manifest deps: langgraph, crewai, pyautogen/autogen, strands-agents, pydantic-ai,
agno, mcp. Note: langchain_openai.AzureChatOpenAI / AzureOpenAIEmbeddings are the Azure
forms of the LangChain LLM classes — an agentic LangGraph/CrewAI app on Azure uses these, which is
BOTH an agentic signal (3B) AND an azure_openai AI signal (3.1/3.2).
is_agentic: true, framework = detected.is_agentic: true, framework: "custom".is_agentic: true, framework: "custom".is_agentic: false.is_agentic: false.is_agentic: false.Multiple frameworks → framework = the one with the most agent definitions; record the others in
detection_signals. If is_agentic: true, run the agentic extraction (Step 5.5) and tool manifest
(Step 6.5) after Step 5.
Confidence: LLM SDK + a second strong signal → 95%+; any one strong signal (Azure OpenAI, OpenAI, Anthropic, an Azure Cognitive client) → 90%+; weak-only → 60–70%; none → 0%.
False-positive checklist: Azure AI Search alone is NOT AI (require embeddings imports —
SearchClient for keyword search is a regular index); azure-identity alone is auth, not AI;
dead/commented code excluded.
Exit gate — confidence < 70%:
ai-workload-profile.json already exists with metadata.profile_source: "iac_cognitive" (from
discover-iac.md‘s Cognitive-Services path): exit cleanly, do not modify it — the
IaC-inferred profile is retained. Report signals + confidence.ai-workload-profile.json. Report signals, confidence, and reason.Confidence ≥ 70%: continue to Steps 5–8.
For each signal, extract model IDs and capabilities.
Azure OpenAI note — deployment name vs model name. Azure OpenAI calls pass a deployment
name at call time (model=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"] or a literal like
"gpt-4o-prod"), not the base model name. Record the deployment string as model_id when that is
all the code exposes, and record the underlying base model (gpt-4o, gpt-4.1, text-embedding-3-large)
when a Terraform azurerm_cognitive_deployment or a comment reveals it. Note in usage_context
that the id is a deployment name when applicable.
Model-string patterns: gpt-*, o1*/o3*/o4*, text-embedding-*, dall-e-*/gpt-image-*,
whisper-*, tts-* (OpenAI/Azure OpenAI); claude-* (Anthropic). Capabilities from API calls:
text_generation (chat.completions.create), streaming (stream=True), function_calling
(tools=), vision (image input), embeddings (embeddings.create / AzureOpenAIEmbeddings),
structured_output (response_format), image_generation (images.generate), speech_to_text /
text_to_speech (audio APIs, Azure Speech). Cognitive clients → document_extraction
(Document Intelligence), image_analysis (Vision), speech_transcription (Speech).
Load references/vendored/ai/sdk-capability-map.json. For each AI call site: resolve the SDK method,
detect structured output (for the structured-output trio: response_format / responseSchema),
assign capability, collapse by (model_id, sdk_method, structured_output) into one workload with all
call_sites. workload_id = "wl_" + sha256(model_id + "|" + sdk_method + "|" + ("structured" if structured_output else "plain"))[:6]. Azure OpenAI apps call the SAME openai.* SDK methods, so the
capability map covers them unchanged. Every workloads[].model_id must appear in models[].
Per agent: agent_id (snake-cased), file, line, model_id, tools[], memory_type
(conversation_buffer/rag/none/unknown), role. Classify orchestration_pattern
(single/hierarchical/swarm/graph/sequential/unknown — StateGraph/add_edge → graph;
CrewAI Process.sequential → sequential, Process.hierarchical → hierarchical). Set
has_memory, memory_backend (redis/postgres/in_memory/vector_store/unknown),
has_human_in_loop.
primary_sdk (openai / @azure/openai / azure-ai-inference / Azure.AI.OpenAI / an Azure
Cognitive SDK), sdk_version, frameworks[] (LangChain/LlamaIndex/Semantic Kernel/none),
languages[], pattern (direct_sdk/framework/rest_api/mixed), gateway_type
(llm_router for LiteLLM/OpenRouter/Portkey/Helicone — including an azure/<deployment> litellm
model string; api_gateway; voice_platform; framework; direct; or null — do NOT read secret
values, only key presence), and the boolean capabilities_summary map. ## Step 6.5 tool manifest
(only if agentic): per tool name, file, line, transport (function/api/mcp/unknown),
auth_hint (none/api_key/oauth/iam/unknown), used_by_agents[].
Extract Azure AI infra: azurerm_cognitive_account, azurerm_cognitive_deployment,
azurerm_machine_learning_workspace, azurerm_search_service, plus managed identities / Key Vault
entries referenced by AI code. { address, type, file, config }. infrastructure: [] when no IaC.
Build the profile per references/shared/schema-discover-ai.md. ai_source:
azure_openai — Azure OpenAI SDK/deployments detected (the 3.1–3.3 Azure signals).openai — direct OpenAI SDK, no azure config (3.4).anthropic — Anthropic only.both — Azure-OpenAI/OpenAI + another provider (e.g. Anthropic).other — traditional ML only (Vision / Document Intelligence / Speech / Language / Azure ML) with
no LLM SDK.profile_source: application_code (fresh from code), iac_cognitive (pre-existing IaC-inferred),
or merged (both — code wins on conflict; union infrastructure[] by address; set
sources_analyzed.terraform/.application_code accordingly).
CRITICAL field names (exact): model_id, service, detected_via, capabilities_used,
usage_context, pattern, gateway_type, capabilities_summary, ai_source. The field is
service, never azure_service. Conditional sections: current_costs only if billing ran;
infrastructure: [] if no IaC; agentic_profile/tool_manifest only if is_agentic.
The assembler (discover-assemble.md) writes the file; do not update .phase-status.json here.
summary.ai_source ∈ azure_openai | openai | anthropic | both | other (never gemini).metadata.profile_source ∈ application_code | iac_cognitive | merged.models[] entry has the exact-named fields; detected_via lists only analyzed sources.workloads[] present; every workloads[].model_id ∈ models[].model_id; workload_id
matches wl_[0-9a-f]{6}.integration.gateway_type set (or null); capabilities_summary consistent with
models[].capabilities_used.is_agentic cases: agentic_profile+tool_manifest present iff true; agent_count ==
agents[] length; tool_count == deduped tool_manifest[] length..env* never read.Discover & analysis ONLY. Do NOT name AWS services, recommend Bedrock models, estimate cost, or plan migration — that is Design/Estimate/Generate. This fragment inventories what runs on Azure.
Net-new for azure (gcp had discover-app-code.md; azure had none). This is what makes AI
detectable WITHOUT IaC — the common case, since most startups have AzureOpenAI in code but no
azurerm_cognitive_account in Terraform. Produces ai-workload-profile.json, the artifact the
whole AI track (clarify-ai → design-ai → estimate-ai → generate-artifacts-ai) reads. Test corpus:
data/azure/ai-workloads/.