|
|
|
|
@@ -24,9 +24,9 @@ You have three companion documents for this port:
|
|
|
|
|
|
|
|
|
|
The Natural Language XVA Pricer is a chat-based interface that lets the CVA desk interact with an AI agent to price trades using natural language. It serves two modes: **general chat** (ask questions about XVA pricing, get explanations) and **email extraction** (upload a sales email, get structured trade data back as JSON).
|
|
|
|
|
|
|
|
|
|
The data flows like this: The user types a message or drops an email `.html` file onto the chat area. The Blazor WASM client sends the request to the ASP.NET Core backend via HTTP POST. The backend processes it through **Microsoft Semantic Kernel** — an AI orchestration framework that connects to an OpenAI-compatible LLM proxy (CLIProxyAPI running locally). For extraction requests, the backend prepends **few-shot examples** (real email → expected JSON pairs loaded from disk) to teach the model the expected output format. The LLM can autonomously call **validation tools** (counterparty lookup, trade ID validation, currency validation, schema validation) via SK's automatic function calling. The response streams back token-by-token as **Server-Sent Events (SSE)**, and the client renders each token into the chat UI with **markdown formatting** and **XSS sanitization**.
|
|
|
|
|
The data flows like this: The user types a message or drops an email `.html` file onto the chat area. The Blazor WASM client sends the request to the ASP.NET Core backend via HTTP POST. The backend processes it through **Microsoft Semantic Kernel** — an AI orchestration framework that connects to **Azure OpenAI** using Azure AD authentication (the same tenant CRC already uses). For extraction requests, the backend prepends **few-shot examples** (real email → expected JSON pairs loaded from disk) to teach the model the expected output format. The LLM can autonomously call **validation tools** (counterparty lookup, trade ID validation, currency validation, schema validation) via SK's automatic function calling. The response streams back token-by-token as **Server-Sent Events (SSE)**, and the client renders each token into the chat UI with **markdown formatting** and **XSS sanitization**.
|
|
|
|
|
|
|
|
|
|
The external dependencies are: (1) a CLIProxyAPI proxy for LLM access (any OpenAI-compatible endpoint works), (2) three external APIs for validation (counterparty, trade, currency) — these are the existing CRC backend services that CRC.Server already integrates with, and (3) the `Markdig` NuGet package for markdown rendering plus `Microsoft.SemanticKernel` for LLM orchestration.
|
|
|
|
|
The external dependencies are: (1) an Azure OpenAI resource with a deployed model (authenticated via Azure AD tenant ID), (2) three external APIs for validation (counterparty, trade, currency) — these are the existing CRC backend services that CRC.Server already integrates with, and (3) the `Markdig` NuGet package for markdown rendering plus `Microsoft.SemanticKernel` for LLM orchestration.
|
|
|
|
|
|
|
|
|
|
**The one thing you must understand**: this feature is an isolated page. It doesn't need Fluxor, doesn't modify CRC's data layer, and doesn't touch the Pricer/MarketData/XVA/Sales pages. It adds a controller, some services, a page, and a nav link. If something goes wrong during porting, the blast radius is limited to the new files.
|
|
|
|
|
|
|
|
|
|
@@ -34,20 +34,25 @@ The external dependencies are: (1) a CLIProxyAPI proxy for LLM access (any OpenA
|
|
|
|
|
|
|
|
|
|
## Design Decisions (Detailed)
|
|
|
|
|
|
|
|
|
|
### 1. Semantic Kernel over raw HttpClient for LLM communication
|
|
|
|
|
### 1. Semantic Kernel with Azure OpenAI for LLM communication
|
|
|
|
|
|
|
|
|
|
**What we chose:** Microsoft Semantic Kernel (SK) as the AI orchestration layer.
|
|
|
|
|
**What we chose:** Microsoft Semantic Kernel (SK) as the AI orchestration layer, connecting to Azure OpenAI via Azure AD authentication.
|
|
|
|
|
|
|
|
|
|
**Why:** The core value isn't just chat — it's the **extraction agent loop**. The agent extracts trade data, calls validation tools, interprets results, retries with fixes, and escalates to the user. Without SK, you'd need to: (a) manually parse the LLM's tool-call JSON from the streaming response, (b) dispatch to the correct C# function, (c) serialize the result, (d) feed it back to the LLM, (e) handle the loop termination. SK does all of this with one line: `FunctionChoiceBehavior.Auto()`. It turns ~200 lines of manual orchestration into zero.
|
|
|
|
|
|
|
|
|
|
Azure OpenAI is the LLM backend because CRC's sandbox environment provides it with an Azure AD tenant. `DefaultAzureCredential` integrates with CRC's existing Azure AD auth — no separate API keys to manage. On a developer's machine it uses the `az login` token; in production it can use managed identity.
|
|
|
|
|
|
|
|
|
|
**What we rejected:**
|
|
|
|
|
- **Raw HttpClient + manual SSE parsing** — This was the original Phase 2 approach. It works for simple chat but doesn't support tool calling without writing a full agent loop. Rejected when we added extraction tools.
|
|
|
|
|
- **LangChain/.NET equivalent** — Considered briefly. SK is Microsoft's official offering, has first-class .NET support, and integrates cleanly with ASP.NET Core DI. LangChain's .NET port was less mature.
|
|
|
|
|
- **Azure OpenAI Service directly** — CRC's network may not allow direct Azure OpenAI access from the server. CLIProxyAPI acts as a local proxy, and SK's OpenAI connector targets any OpenAI-compatible endpoint.
|
|
|
|
|
- **OpenAI direct (non-Azure)** — CRC's network may not allow direct OpenAI access. Azure OpenAI is within the corporate Azure tenant, which is already permitted.
|
|
|
|
|
- **API key auth** — Simpler to configure but keys need rotation and secure storage. Azure AD tokens are automatic and tied to the developer/service identity.
|
|
|
|
|
|
|
|
|
|
**When you'd revisit this:** If CRC moves to Azure OpenAI with managed identity auth, you'd swap `AddOpenAIChatCompletion()` for `AddAzureOpenAIChatCompletion()`. SK makes this a one-line change.
|
|
|
|
|
**When you'd revisit this:** If the Azure OpenAI resource is decommissioned or you need a different model provider, swap `AddAzureOpenAIChatCompletion()` for `AddOpenAIChatCompletion()` — SK abstracts the difference. Everything downstream (controller, plugins, streaming) stays identical.
|
|
|
|
|
|
|
|
|
|
**Target adaptation:** CRC uses Scrutor for assembly scanning. SK's `AddKernel()` and `AddOpenAIChatCompletion()` are explicit registrations that coexist with Scrutor — no conflict. But verify that Scrutor doesn't auto-register ExtractionPlugin before your manual `AddScoped<ExtractionPlugin>()` call (it could if it scans the Plugins namespace). If it does, you'll get the plugin registered without its HttpClient dependencies. Check by looking at CRC's Scrutor scan filters.
|
|
|
|
|
**Target adaptation:** CRC uses Scrutor for assembly scanning. SK's `AddKernel()` and `AddAzureOpenAIChatCompletion()` are explicit registrations that coexist with Scrutor — no conflict. But verify that Scrutor doesn't auto-register ExtractionPlugin before your manual `AddScoped<ExtractionPlugin>()` call (it could if it scans the Plugins namespace). If it does, you'll get the plugin registered without its HttpClient dependencies. Check by looking at CRC's Scrutor scan filters.
|
|
|
|
|
|
|
|
|
|
**Azure AD prerequisite:** Developers must run `az login --tenant <tenant-id>` before starting CRC.Server. `DefaultAzureCredential` will silently fail at the first LLM call (not at startup) if the token isn't available — the error message mentions "ManagedIdentityCredential" and "EnvironmentCredential" failures, which can be confusing. The fix is always `az login`.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@@ -200,11 +205,14 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
|
|
|
|
|
**Step-by-step:**
|
|
|
|
|
1. Add `Microsoft.SemanticKernel` to `CRC.Server.csproj`
|
|
|
|
|
2. Add `Markdig` to `CRC.Client.csproj` (check if it's already there: `grep -i markdig CRC.Client.csproj`)
|
|
|
|
|
3. Run `dotnet restore CRC.sln`
|
|
|
|
|
2. Add `Microsoft.SemanticKernel.Connectors.AzureOpenAI` to `CRC.Server.csproj`
|
|
|
|
|
3. Add `Azure.Identity` to `CRC.Server.csproj` (check first: `grep -i Azure.Identity CRC.Server.csproj` — CRC may already have it since it uses Azure AD)
|
|
|
|
|
4. Add `Markdig` to `CRC.Client.csproj` (check if it's already there: `grep -i markdig CRC.Client.csproj`)
|
|
|
|
|
5. Run `dotnet restore CRC.sln`
|
|
|
|
|
|
|
|
|
|
**Expected friction on target:**
|
|
|
|
|
- **GV Artifactory may not have `Microsoft.SemanticKernel`**. SK is a relatively new package. If it's not mirrored in the internal feed, you'll need to either: request it be added to Artifactory, or temporarily add nuget.org as a source in `nuget.config` (check with your team if this is allowed).
|
|
|
|
|
- **Azure.Identity version conflict**. If CRC already has `Azure.Identity` at a different version, the SK transitive dependency may conflict. Run `dotnet list CRC.Server package --include-transitive | grep Azure.Identity` to check.
|
|
|
|
|
- **Version pinning**. CRC uses `RestorePackagesWithLockFile=true` — after installing, commit the updated `packages.lock.json`.
|
|
|
|
|
|
|
|
|
|
**Verify it works:**
|
|
|
|
|
@@ -348,37 +356,101 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### T6: Register Semantic Kernel
|
|
|
|
|
### T6: Register Semantic Kernel with Azure OpenAI
|
|
|
|
|
|
|
|
|
|
**Prerequisites:** T1 (NuGet package installed).
|
|
|
|
|
**Prerequisites:** T1 (NuGet packages installed). Developer has run `az login --tenant <tenant-id>`.
|
|
|
|
|
|
|
|
|
|
**Context:** This registers the SK Kernel and OpenAI chat completion connector in DI. The connector works with any OpenAI-compatible API, so we point it at CLIProxyAPI (a local proxy that routes to Claude/GPT).
|
|
|
|
|
**Context:** This registers the SK Kernel and Azure OpenAI chat completion connector in DI. Unlike the source project (which used a local proxy), the CRC sandbox uses Azure OpenAI with Azure AD authentication. The key differences: use `AddAzureOpenAIChatCompletion()` (not `AddOpenAIChatCompletion()`), use deployment name (not model name), endpoint has NO `/v1` suffix, and auth uses `DefaultAzureCredential` with the tenant ID.
|
|
|
|
|
|
|
|
|
|
**Step-by-step:**
|
|
|
|
|
1. Add `using Microsoft.SemanticKernel;` to the startup file
|
|
|
|
|
2. Read config values from `NlxvaPricer:*` section
|
|
|
|
|
3. Register: `AddOpenAIChatCompletion()` then `AddKernel()`
|
|
|
|
|
4. The base URL **MUST** include `/v1` — this is the most common misconfiguration
|
|
|
|
|
1. Add `using Microsoft.SemanticKernel;` and `using Azure.Identity;` to the startup file
|
|
|
|
|
2. Read config values from `NlxvaPricer:*` section (AzureOpenAIEndpoint, DeploymentName, TenantId)
|
|
|
|
|
3. Register: `AddAzureOpenAIChatCompletion()` then `AddKernel()`
|
|
|
|
|
4. The endpoint is the Azure resource URL — do NOT add `/v1` (the Azure SDK handles path construction)
|
|
|
|
|
5. Use `DefaultAzureCredential` with the tenant ID
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
var azureEndpoint = builder.Configuration["NlxvaPricer:AzureOpenAIEndpoint"];
|
|
|
|
|
var deploymentName = builder.Configuration["NlxvaPricer:DeploymentName"];
|
|
|
|
|
var tenantId = builder.Configuration["NlxvaPricer:TenantId"];
|
|
|
|
|
|
|
|
|
|
builder.Services.AddAzureOpenAIChatCompletion(
|
|
|
|
|
deploymentName: deploymentName,
|
|
|
|
|
endpoint: azureEndpoint,
|
|
|
|
|
credentials: new DefaultAzureCredential(
|
|
|
|
|
new DefaultAzureCredentialOptions { TenantId = tenantId }));
|
|
|
|
|
builder.Services.AddKernel();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Expected friction on target:**
|
|
|
|
|
- **CLIProxyAPI availability**: The proxy must be running on the target machine at the configured URL. If CRC's server runs on a different machine than the developer's laptop (where CLIProxyAPI runs), you'll need network routing or to deploy CLIProxyAPI alongside CRC.
|
|
|
|
|
- **API key**: CLIProxyAPI may not check the key, but the SK OpenAI connector requires a non-empty string. Use `"not-needed"` as a placeholder.
|
|
|
|
|
- **`az login` not done**: `DefaultAzureCredential` tries multiple auth methods in sequence (environment vars → managed identity → Visual Studio → Azure CLI → etc.). On a developer machine, it relies on Azure CLI. If the developer hasn't run `az login --tenant <tenant-id>`, the error at runtime will be a confusing `CredentialUnavailableException` listing all the methods it tried. The fix is always: `az login --tenant <tenant-id>`.
|
|
|
|
|
- **Deployment name vs model name**: In Azure portal, you deploy a model (e.g., `gpt-4o`) and give the deployment a name (e.g., `gpt4o-prod`). You pass the **deployment name** to SK, not the model name. Ask your Azure admin for the deployment name.
|
|
|
|
|
- **Azure RBAC permissions**: The developer's Azure AD identity needs the "Cognitive Services OpenAI User" role on the Azure OpenAI resource. Without it, you'll get a 403.
|
|
|
|
|
|
|
|
|
|
**Verify it works:**
|
|
|
|
|
- `dotnet build` succeeds (SK NuGet resolved correctly)
|
|
|
|
|
- `dotnet build` succeeds
|
|
|
|
|
- At runtime: inject `Kernel` into a test controller and verify it resolves
|
|
|
|
|
- Quick smoke test: call `kernel.GetRequiredService<IChatCompletionService>()` — should not throw
|
|
|
|
|
- Quick smoke test: `kernel.GetRequiredService<IChatCompletionService>()` — should not throw
|
|
|
|
|
- Full test: the diagnostic stream-test endpoint (see T6b below)
|
|
|
|
|
|
|
|
|
|
**If it breaks — diagnostic checklist:**
|
|
|
|
|
- Symptom: 404 on LLM requests
|
|
|
|
|
Cause: Base URL missing `/v1`
|
|
|
|
|
Fix: Change `http://localhost:8317` to `http://localhost:8317/v1`
|
|
|
|
|
- Symptom: `HttpRequestException: Connection refused`
|
|
|
|
|
Cause: CLIProxyAPI not running
|
|
|
|
|
Fix: Start CLIProxyAPI on the target machine, verify with `curl http://localhost:8317/v1/models`
|
|
|
|
|
- Symptom: `CredentialUnavailableException` with "DefaultAzureCredential failed to retrieve a token"
|
|
|
|
|
Cause: Developer not logged in to Azure CLI
|
|
|
|
|
Fix: Run `az login --tenant <tenant-id>`, then restart CRC.Server
|
|
|
|
|
- Symptom: HTTP 403 Forbidden from Azure OpenAI
|
|
|
|
|
Cause: Azure AD identity lacks "Cognitive Services OpenAI User" role
|
|
|
|
|
Fix: Ask Azure admin to grant the role on the Azure OpenAI resource
|
|
|
|
|
- Symptom: HTTP 404 on Azure OpenAI endpoint
|
|
|
|
|
Cause: Wrong deployment name, or deployment doesn't exist
|
|
|
|
|
Fix: Verify deployment name in Azure portal → Azure OpenAI → Deployments
|
|
|
|
|
- Symptom: `InvalidOperationException: No service for type IChatCompletionService`
|
|
|
|
|
Cause: `AddOpenAIChatCompletion()` not called before `AddKernel()`
|
|
|
|
|
Fix: Ensure registration order: OpenAIChatCompletion first, then Kernel
|
|
|
|
|
Cause: `AddAzureOpenAIChatCompletion()` not called before `AddKernel()`
|
|
|
|
|
Fix: Ensure registration order: AzureOpenAIChatCompletion first, then Kernel
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### T6b: Verify streaming hop 1 (Azure OpenAI → CRC.Server)
|
|
|
|
|
|
|
|
|
|
**Prerequisites:** T6 (SK registered), T7 (controller exists — or add the diagnostic endpoint to any controller temporarily).
|
|
|
|
|
|
|
|
|
|
**Context:** Before building the full UI, verify that tokens actually stream from Azure OpenAI through CRC.Server. This catches buffering issues early (response compression middleware, Azure API Management, corporate proxies).
|
|
|
|
|
|
|
|
|
|
**Step-by-step:**
|
|
|
|
|
1. Add a temporary diagnostic endpoint to NlxvaPricerController (see Critical Pattern #8 in export-spec)
|
|
|
|
|
2. Run: `curl -N https://localhost:7100/api/nlxva-pricer/stream-test`
|
|
|
|
|
3. Watch the timestamps in the output
|
|
|
|
|
|
|
|
|
|
**What correct streaming looks like:**
|
|
|
|
|
```
|
|
|
|
|
data: [450ms] 1 ← timestamps spread across seconds
|
|
|
|
|
data: [620ms]
|
|
|
|
|
data: [780ms] 2
|
|
|
|
|
data: [950ms]
|
|
|
|
|
data: [1100ms] 3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**What buffered streaming looks like:**
|
|
|
|
|
```
|
|
|
|
|
data: [8200ms] 1 ← all timestamps clustered at the end
|
|
|
|
|
data: [8201ms]
|
|
|
|
|
data: [8202ms] 2
|
|
|
|
|
data: [8203ms]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**If buffered — check these in order:**
|
|
|
|
|
1. **Response compression middleware**: If CRC.Server has `app.UseResponseCompression()`, it buffers SSE to compress. Add `Response.Headers["Content-Encoding"] = "identity";` in the controller to opt out.
|
|
|
|
|
2. **Azure API Management (APIM)**: If APIM sits in front of the Azure OpenAI resource, it buffers by default. Need `forward-request` policy with `buffer-response="false"`.
|
|
|
|
|
3. **Corporate HTTPS proxy**: Check `echo $HTTPS_PROXY` on the server. May need proxy bypass for `*.openai.azure.com`.
|
|
|
|
|
4. **IIS**: If CRC runs under IIS, add `responseBufferLimit="0"` in web.config.
|
|
|
|
|
|
|
|
|
|
**Always set these headers on SSE endpoints:**
|
|
|
|
|
```csharp
|
|
|
|
|
Response.ContentType = "text/event-stream";
|
|
|
|
|
Response.Headers["Cache-Control"] = "no-cache";
|
|
|
|
|
Response.Headers["X-Accel-Buffering"] = "no"; // prevents NGINX buffering
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. Remove the diagnostic endpoint after verification.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
@@ -561,10 +633,11 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
|
|
|
|
|
**Step-by-step checklist:**
|
|
|
|
|
|
|
|
|
|
- [ ] `NlxvaPricer:LlmBaseUrl` in CRC.Server `appsettings.json` — default `http://localhost:8317/v1`
|
|
|
|
|
- [ ] `NlxvaPricer:LlmModel` in CRC.Server `appsettings.json` — default `claude-sonnet-4-6`
|
|
|
|
|
- [ ] `NlxvaPricer:LlmApiKey` in CRC.Server `appsettings.json` — default `not-needed`
|
|
|
|
|
- [ ] `NlxvaPricer:AzureOpenAIEndpoint` in CRC.Server `appsettings.json` — e.g., `https://your-resource.openai.azure.com/` — **no `/v1`**. What happens if missing: SK registration fails at startup
|
|
|
|
|
- [ ] `NlxvaPricer:DeploymentName` in CRC.Server `appsettings.json` — the Azure deployment name (not model name). Get from Azure portal → Azure OpenAI → Deployments
|
|
|
|
|
- [ ] `NlxvaPricer:TenantId` in CRC.Server `appsettings.json` — Azure AD tenant ID. Same tenant CRC uses for Microsoft.Identity.Web auth
|
|
|
|
|
- [ ] `NlxvaPricer:FewShotPath` in CRC.Server `appsettings.json` — default `examples/extraction`
|
|
|
|
|
- [ ] Developer has run `az login --tenant <tenant-id>` — `DefaultAzureCredential` needs this. Failure shows at first LLM call, not at startup
|
|
|
|
|
- [ ] `ExternalApis:CounterpartyBaseUrl` — default `http://localhost:5000/api/counterparty` (or use CRC's existing)
|
|
|
|
|
- [ ] `ExternalApis:TradeBaseUrl` — default `http://localhost:5000/api/trade` (or use CRC's existing)
|
|
|
|
|
- [ ] `ExternalApis:CurrencyBaseUrl` — default `http://localhost:5000/api/currency` (or use CRC's existing)
|
|
|
|
|
@@ -579,7 +652,7 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
**Full verification sequence:**
|
|
|
|
|
|
|
|
|
|
1. `dotnet build --configuration release CRC.sln` — 0 errors, 0 new warnings
|
|
|
|
|
2. Start CLIProxyAPI on target machine
|
|
|
|
|
2. Ensure developer has run `az login --tenant <tenant-id>`
|
|
|
|
|
3. Start CRC.Server
|
|
|
|
|
4. Navigate to CRC.Client in browser
|
|
|
|
|
5. Verify "NL XVA Pricer" appears in sidebar
|
|
|
|
|
@@ -598,7 +671,7 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
|
|
|
|
|
| # | Symptom | Likely Cause | Fix |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| 1 | 404 on `/v1/chat/completions` | Base URL missing `/v1` suffix | Set `NlxvaPricer:LlmBaseUrl` to `http://localhost:8317/v1` |
|
|
|
|
|
| 1 | 404 on Azure OpenAI endpoint | Wrong deployment name or endpoint URL | Verify deployment name in Azure portal; endpoint should be `https://<resource>.openai.azure.com/` with NO `/v1` |
|
|
|
|
|
| 2 | CORS 403 in browser console | CORS policy doesn't cover CRC.Client origin or `text/event-stream` | Add CRC.Client origin with `AllowAnyHeader()` in CORS config |
|
|
|
|
|
| 3 | No streaming — entire response at once | `SetBrowserResponseStreamingEnabled(true)` missing on client | Add to HttpRequestMessage before SendAsync |
|
|
|
|
|
| 4 | `NotSupportedException: Synchronous operations` | Using `reader.EndOfStream` in WASM | Replace with `while ((line = await ReadLineAsync()) != null)` |
|
|
|
|
|
@@ -611,7 +684,10 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
| 11 | `FileNotFoundException` for instruction-template.txt | Examples folder not at ContentRootPath | Log ContentRootPath; verify examples location; update FewShotPath config |
|
|
|
|
|
| 12 | Empty few-shot examples (only system message) | Subdirectory structure wrong | Verify `examples/extraction/few-shot/01/input.html` exists |
|
|
|
|
|
| 13 | `NuGet restore error` for SemanticKernel | Package not in GV Artifactory feed | Request mirroring or temporary nuget.org source |
|
|
|
|
|
| 14 | `HttpRequestException: Connection refused` | CLIProxyAPI not running | Start proxy; verify with `curl http://localhost:8317/v1/models` |
|
|
|
|
|
| 14 | `CredentialUnavailableException` from DefaultAzureCredential | Developer not logged in via Azure CLI | Run `az login --tenant <tenant-id>`, restart CRC.Server |
|
|
|
|
|
| 14b | HTTP 403 from Azure OpenAI | Azure AD identity lacks role | Grant "Cognitive Services OpenAI User" on the Azure OpenAI resource |
|
|
|
|
|
| 14c | All tokens arrive at once (no streaming) | Response compression or proxy buffering | Use stream-test diagnostic endpoint; check `UseResponseCompression()`; set `X-Accel-Buffering: no` header |
|
|
|
|
|
| 14d | Streaming works in curl but not in browser | Response compression only applied for browser Accept-Encoding | Add `Response.Headers["Content-Encoding"] = "identity"` in SSE endpoints |
|
|
|
|
|
| 15 | Drag-drop file not triggering extraction | `file-drop.js` not loaded | Check `<script>` tag in index.html; check browser console for JS errors |
|
|
|
|
|
| 16 | `window.fileDrop is undefined` | Script loaded after Blazor framework init | Move `<script>` tag before `_framework/blazor.webassembly.js` |
|
|
|
|
|
| 17 | `JsonException` when parsing SSE data | SSE line doesn't match expected format | Add logging for raw SSE lines; check server-side WriteSSEAsync format |
|
|
|
|
|
@@ -627,6 +703,20 @@ Check CRC's MudBlazor version first: `grep MudBlazor CRC.Client.csproj`
|
|
|
|
|
- **NuGet source:** Available on nuget.org. If CRC's GV Artifactory doesn't mirror it, this is a blocker — request mirroring.
|
|
|
|
|
- **Size:** ~5MB total with dependencies
|
|
|
|
|
|
|
|
|
|
### Microsoft.SemanticKernel.Connectors.AzureOpenAI
|
|
|
|
|
- **Why needed:** Azure OpenAI-specific connector for SK (provides `AddAzureOpenAIChatCompletion()`)
|
|
|
|
|
- **.NET compatibility:** Same as core SK package
|
|
|
|
|
- **Transitive dependencies:** Pulls in `Azure.AI.OpenAI` SDK
|
|
|
|
|
- **NuGet source:** Same as core SK — nuget.org
|
|
|
|
|
- **Note:** This is separate from the core SK package. Without it, only `AddOpenAIChatCompletion()` is available (for non-Azure endpoints).
|
|
|
|
|
|
|
|
|
|
### Azure.Identity
|
|
|
|
|
- **Why needed:** Provides `DefaultAzureCredential` for Azure AD authentication to Azure OpenAI
|
|
|
|
|
- **.NET compatibility:** .NET Standard 2.0+ (compatible with everything)
|
|
|
|
|
- **CRC likely already has this** — it uses `Microsoft.Identity.Web` for Azure AD auth. Check `grep Azure.Identity CRC.Server.csproj`.
|
|
|
|
|
- **Version conflicts:** If CRC has an older version, SK may pull in a newer one. Usually compatible, but verify with `dotnet build`.
|
|
|
|
|
- **NuGet source:** Available on nuget.org and commonly mirrored in enterprise feeds
|
|
|
|
|
|
|
|
|
|
### Markdig (1.1.1)
|
|
|
|
|
- **Why needed:** Markdown → HTML conversion for rendering LLM responses
|
|
|
|
|
- **.NET compatibility:** .NET Standard 2.0+ (compatible with everything)
|
|
|
|
|
@@ -664,8 +754,10 @@ If the feature needs to be removed:
|
|
|
|
|
|
|
|
|
|
**NuGet packages to remove:**
|
|
|
|
|
- `Microsoft.SemanticKernel` from CRC.Server
|
|
|
|
|
- `Microsoft.SemanticKernel.Connectors.AzureOpenAI` from CRC.Server
|
|
|
|
|
- `Azure.Identity` from CRC.Server (only if not used by other CRC features — likely IS used, so leave it)
|
|
|
|
|
- `Markdig` from CRC.Client (if not used by other features)
|
|
|
|
|
|
|
|
|
|
**Config keys to remove:**
|
|
|
|
|
- `NlxvaPricer:*` section from `appsettings.json`
|
|
|
|
|
- `NlxvaPricer:*` section (AzureOpenAIEndpoint, DeploymentName, TenantId, FewShotPath) from `appsettings.json`
|
|
|
|
|
- `ExternalApis:*` section (if only used by this feature)
|
|
|
|
|
|