
Syncfusion Blazor Ai Assistview
- 242 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-ai-assistview for development tasks
About
syncfusion-blazor-ai-assistview: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-ai-assistview
Syncfusion Blazor Ai Assistview by the numbers
- 242 all-time installs (skills.sh)
- +14 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,612 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/blazor-ui-components-skills --skill syncfusion-blazor-ai-assistviewAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 242 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-ai-assistview for development tasks
Files
Syncfusion Blazor AI AssistView
Build conversational AI interfaces with the Syncfusion Blazor AI AssistView component. This skill provides complete guidance for creating interactive chat applications, AI assistant UIs, and prompt-response systems in Blazor Server, WebAssembly, and Web App projects.
Component Overview
The SfAIAssistView component is a specialized UI control for building AI-powered chat applications. It manages the presentation and interaction model for prompt-response conversations with built-in support for:
- Prompt Management: Input text, suggestions, placeholder text
- Response Handling: Async processing, markdown rendering, streaming support
- Customization: Avatar icons, styling, theme integration
- UX Features: Scroll-to-bottom indicator, conversation history
- Event Integration: PromptRequested events for AI service callbacks
Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and NuGet package setup
- Basic component structure and initialization
- PromptRequested event handler implementation
- Minimal working example with async responses
Prompt Configuration
📄 Read: references/prompt-configuration.md
- Setting initial prompt text
- Configuring prompt placeholder text
- Managing prompt-response collections
- Handling prompt events asynchronously
Prompt Suggestions
📄 Read: references/prompt-suggestions.md
- Adding suggestion lists for user guidance
- Customizing suggestion headers
- Building suggestion-response mappings
- User interaction patterns with suggestions
Customization & Icons
📄 Read: references/customization-styling.md
- Customizing user avatar with PromptIconCss
- Customizing AI avatar with ResponseIconCss
- Applying theme-specific icons
- CSS class and icon integration
Advanced Features
📄 Read: references/advanced-features.md
- Markdown rendering in AI responses
- Scroll-to-bottom navigation (EnableScrollToBottom)
- Managing conversation history with Prompts collection
- Common patterns and use cases
AI Service Integration
📄 Read: references/ai-service-integration.md
- OpenAI API integration (non-streaming and streaming)
- Azure OpenAI deployment
- Local Ollama LLM hosting
- Custom backend patterns
- Error handling and rate limiting strategies
Attachments
📄 Read: references/attachments.md
- Enabling file attachments
- Configuring attachment settings (file types, size limits)
- Handling attachment events (upload, click, remove)
- Server-side upload implementation
- Security and validation best practices
Multi-View Support
📄 Read: references/multi-view-support.md
- Creating multiple specialized AI views
- Active view management and switching
- View-specific configuration
- Separate conversation histories per view
- Role-based and workspace-based views
Toolbars
📄 Read: references/toolbars.md
- Prompt toolbar customization
- Response toolbar actions (copy, like, regenerate)
- Footer toolbar configuration
- Custom toolbar items and templates
- Handling toolbar events
Templates
📄 Read: references/templates.md
- BannerTemplate for initial/empty state
- PromptItemTemplate for custom prompt rendering
- ResponseItemTemplate for custom response rendering
- PromptSuggestionItemTemplate for custom suggestions
- FooterTemplate and ViewTemplate customization
- Template contexts and available properties
Streaming Responses
📄 Read: references/streaming.md
- Enabling streaming mode
- UpdateResponseAsync for progressive updates
- Real-time response rendering
- Integration with OpenAI, Azure, and Ollama streaming APIs
- Stop responding functionality
- Performance optimization
Methods & Programmatic API
📄 Read: references/methods-api.md
- ExecutePromptAsync for automated prompts
- UpdateResponseAsync for streaming updates
- RefreshUIAsync for manual UI refresh
- ScrollToBottomAsync for scroll control
- Automated workflows and batch processing
Quick Start
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 600px;">
<SfAIAssistView
Prompt="What is Blazor?"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Add delay to simulate AI processing
await Task.Delay(1000);
// Connect to your AI service here (OpenAI, Azure, etc.)
var response = await GetAIResponse(args.Prompt);
args.Response = response;
}
private async Task<string> GetAIResponse(string prompt)
{
// Call your AI service API
// For testing: return a simple response
return $"<div>Processing your prompt: {prompt}</div>";
}
}Common Patterns
Pattern 1: Pre-Initialized Conversation
Load the component with existing conversation history:
<SfAIAssistView
Prompts="@conversationHistory"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
@code {
private List<AssistViewPrompt> conversationHistory = new()
{
new AssistViewPrompt {
Prompt = "What is C#?",
Response = "<div>C# is a modern object-oriented programming language...</div>"
}
};
}Pattern 2: Guided Suggestions
Use suggestions to help users refine their prompts:
<SfAIAssistView
PromptSuggestions="@suggestions"
PromptSuggestionsHeader="Try asking about:"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
@code {
private List<string> suggestions = new()
{
"How does dependency injection work?",
"Explain async/await patterns",
"What are Blazor components?"
};
}Pattern 3: Custom Avatars
Personalize user and AI identities with custom icons:
<SfAIAssistView
PromptIconCss="e-icons e-user"
ResponseIconCss="e-icons e-bullet-2"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Key Props
| Property | Type | Purpose |
|---|---|---|
Prompt | string | Initial prompt text to display |
PromptPlaceholder | string | Placeholder text in input field (default: "Type prompt for assistance...") |
PromptSuggestions | List\<string\> | List of suggested prompts for user guidance |
PromptSuggestionsHeader | string | Header text above suggestions |
Prompts | List\<AssistViewPrompt\> | Collection of prompt-response pairs (conversation history) |
PromptIconCss | string | CSS classes for user avatar icon |
ResponseIconCss | string | CSS classes for AI avatar icon (default: "e-assistview-icon") |
EnableScrollToBottom | bool | Show scroll-to-bottom indicator (default: true) |
EnableStreaming | bool | Enable streaming mode for progressive responses |
AttachmentSettings | AssistViewAttachmentSettings | File attachment configuration |
ActiveView | int | Index of currently active view (for multi-view) |
ShowHeader | bool | Show/hide component header (default: true) |
Width | string | Component width (default: "100%") |
Height | string | Component height (default: "100%") |
CssClass | string | Custom CSS classes for styling |
EnableRtl | bool | Enable right-to-left layout (default: false) |
ID | string | Sets the id attribute for the component |
Common Use Cases
1. Customer Support Chatbot - Combine with knowledge base to answer FAQs 2. Coding Assistant - Real-time code suggestions and explanations 3. Content Generator - Interactive writing and editing assistant 4. Learning Interface - Q&A system for educational content 5. API Documentation - Interactive API helper with examples
Advanced Features
Table of Contents
Markdown Rendering
The AI AssistView supports rendering responses as Markdown content, which is automatically converted to HTML. This enables rich text formatting like bold, italic, headings, lists, code blocks, and links.
Basic Markdown Responses
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 650px;">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
// Response with markdown formatting
var markdownResponse = @"
# Heading 1
## Heading 2
This is **bold** text and this is *italic* text.
### Code Examplevar message = ""Hello Blazor""; Console.WriteLine(message);
### List
- Item 1
- Item 2
- Item 3
### Link
[Visit Syncfusion Docs](https://docs.syncfusion.com)
";
args.Response = markdownResponse;
}
}Markdown Features Supported
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
var response = @"
# Main Title
## Subheading
**Bold Text** | *Italic Text* | ***Bold and Italic***
### Ordered List
1. First step
2. Second step
3. Third step
### Unordered List
- Point A
- Point B
- Point C
### Code Blockpublic class Example { public string Message { get; set; } }
### Inline Code
Use `var x = 10;` to declare a variable.
### Blockquote
> This is an important quote
### Table
| Column 1 | Column 2 |
|----------|----------|
| Value 1 | Value 2 |
### Link
[Documentation](https://example.com)
### Horizontal Rule
---
";
args.Response = response;
}Scroll to Bottom
The EnableScrollToBottom property shows or hides the scroll-to-bottom indicator. By default, this is true. When enabled, a floating icon appears when the user scrolls away from the bottom, allowing them to quickly jump to the latest message.
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 650px;">
<!-- Enable scroll-to-bottom indicator (default: true) -->
<SfAIAssistView
EnableScrollToBottom="true"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Disable Scroll to Bottom
<!-- Disable the floating scroll indicator -->
<SfAIAssistView
EnableScrollToBottom="false"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Scroll Behavior
- User scrolls up: Scroll-to-bottom button appears (if EnableScrollToBottom=true)
- User clicks button: View smoothly scrolls to the latest message
- New message arrives: Auto-scrolls to bottom if user is already there
- User manually scrolls down: Button disappears
Conversation History Management
Manage multiple conversations by storing and retrieving prompt-response pairs:
Store Conversation in Memory
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 650px;">
<SfAIAssistView
Prompts="@conversationHistory"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
<button @onclick="ClearHistory">Clear History</button>
<button @onclick="ExportHistory">Export History</button>
@code {
private List<AssistViewPrompt> conversationHistory = new();
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
var response = await GenerateResponse(args.Prompt);
// Add to conversation history
conversationHistory.Add(new AssistViewPrompt
{
Prompt = args.Prompt,
Response = response
});
args.Response = response;
StateHasChanged();
}
private async Task<string> GenerateResponse(string prompt)
{
// Call AI service
return $"<div>Response to: {prompt}</div>";
}
private void ClearHistory()
{
conversationHistory.Clear();
StateHasChanged();
}
private void ExportHistory()
{
var json = System.Text.Json.JsonSerializer.Serialize(conversationHistory);
// Save or download JSON
}
}Load Previous Conversation
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 650px;">
<SfAIAssistView
Prompts="@conversationHistory"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
<button @onclick="LoadPreviousConversation">Load Previous Chat</button>
@code {
private List<AssistViewPrompt> conversationHistory = new();
protected override async Task OnInitializedAsync()
{
// Load previous conversation from storage
await LoadConversationFromStorage();
}
private async Task LoadConversationFromStorage()
{
// Example: Load from localStorage or database
var savedConversation = await GetSavedConversation();
conversationHistory = savedConversation ?? new();
}
private async Task<List<AssistViewPrompt>?> GetSavedConversation()
{
// Fetch from your persistence layer
return null;
}
private async Task LoadPreviousConversation()
{
await LoadConversationFromStorage();
StateHasChanged();
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
var response = $"<div>Response to: {args.Prompt}</div>";
conversationHistory.Add(new AssistViewPrompt
{
Prompt = args.Prompt,
Response = response
});
args.Response = response;
StateHasChanged();
}
}Streaming Responses
Implement streaming responses where the AI response is progressively displayed as it arrives:
@using Syncfusion.Blazor.InteractiveChat
@inject HttpClient Http
<div style="height: 400px; width: 650px;">
<SfAIAssistView
Prompts="@conversationHistory"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private List<AssistViewPrompt> conversationHistory = new();
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// For streaming, start with placeholder
var streamedResponse = await StreamAIResponse(args.Prompt);
conversationHistory.Add(new AssistViewPrompt
{
Prompt = args.Prompt,
Response = streamedResponse
});
args.Response = streamedResponse;
StateHasChanged();
}
private async Task<string> StreamAIResponse(string prompt)
{
var response = new System.Text.StringBuilder();
try
{
// Call streaming API endpoint
using var httpResponse = await Http.GetAsync($"/api/stream?prompt={Uri.EscapeDataString(prompt)}");
using var stream = await httpResponse.Content.ReadAsStreamAsync();
using var reader = new System.IO.StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
response.Append(line);
// Simulate progressive rendering
await Task.Delay(50);
}
return response.ToString();
}
catch (Exception ex)
{
return $"<div class='error'>Error streaming response: {ex.Message}</div>";
}
}
}Common Patterns
Pattern 1: Multi-Turn Conversation with Context
Build context from conversation history for better AI responses:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Build context from conversation history
var context = BuildContextFromHistory(conversationHistory);
// Send prompt with context to AI service
var response = await CallAIWithContext(args.Prompt, context);
conversationHistory.Add(new AssistViewPrompt
{
Prompt = args.Prompt,
Response = response
});
args.Response = response;
StateHasChanged();
}
private string BuildContextFromHistory(List<AssistViewPrompt> history)
{
var context = new System.Text.StringBuilder();
context.AppendLine("Previous conversation:");
foreach (var item in history.TakeLast(5)) // Use last 5 exchanges
{
context.AppendLine($"User: {item.Prompt}");
context.AppendLine($"Assistant: {item.Response}");
}
return context.ToString();
}
private async Task<string> CallAIWithContext(string prompt, string context)
{
// Call your AI service with both prompt and context
return $"<div>Response considering context</div>";
}Pattern 2: Conversation Persistence
Save conversations to a database for retrieval:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
var response = await GenerateResponse(args.Prompt);
var promptEntry = new AssistViewPrompt
{
Prompt = args.Prompt,
Response = response
};
conversationHistory.Add(promptEntry);
// Persist to database
await SaveToDatabase(promptEntry);
args.Response = response;
StateHasChanged();
}
private async Task SaveToDatabase(AssistViewPrompt entry)
{
var response = await Http.PostAsJsonAsync("/api/conversations", entry);
if (!response.IsSuccessStatusCode)
{
// Handle error
}
}Pattern 3: Rate Limiting and Error Handling
private DateTime lastRequestTime = DateTime.MinValue;
private const int MinIntervalMs = 500;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Rate limiting
var timeSinceLastRequest = DateTime.Now - lastRequestTime;
if (timeSinceLastRequest.TotalMilliseconds < MinIntervalMs)
{
args.Response = "<div class='warning'>Please wait before sending another message</div>";
return;
}
lastRequestTime = DateTime.Now;
try
{
var response = await GenerateResponse(args.Prompt);
args.Response = response;
conversationHistory.Add(new AssistViewPrompt
{
Prompt = args.Prompt,
Response = response
});
}
catch (HttpRequestException)
{
args.Response = "<div class='error'>Connection error. Please try again.</div>";
}
catch (OperationCanceledException)
{
args.Response = "<div class='error'>Request timeout. Please try again.</div>";
}
catch (Exception ex)
{
args.Response = $"<div class='error'>Error: {ex.Message}</div>";
}
StateHasChanged();
}
private async Task<string> GenerateResponse(string prompt)
{
await Task.Delay(1000);
return $"<div>Response to: {prompt}</div>";
}Best Practices
1. Use markdown for rich formatting - improves readability and user experience 2. Enable scroll-to-bottom - helps users see latest messages in long conversations 3. Persist conversation history - allow users to retrieve previous chats 4. Implement streaming - provide real-time feedback for long-running operations 5. Add context to prompts - improve AI response quality by including conversation history 6. Handle errors gracefully - show user-friendly error messages 7. Rate limit requests - prevent abuse and excessive API calls 8. Test with long conversations - ensure performance remains acceptable
AI Service Integration
Table of Contents
- Overview
- Service Integration Patterns
- OpenAI Integration
- Azure OpenAI Integration
- Local Ollama Integration
- Custom Backend Integration
- Error Handling Strategies
- Rate Limiting & Throttling
- Best Practices
Overview
The AI AssistView component is designed to integrate seamlessly with various AI service backends. This guide covers integration patterns for popular AI services including OpenAI, Azure OpenAI, and local Ollama deployments, as well as custom backends.
Supported Integration Patterns:
- OpenAI API - Cloud-hosted GPT models with streaming support
- Azure OpenAI - Enterprise-grade OpenAI deployment on Azure
- Ollama - Local LLM hosting for privacy and offline capability
- Custom Backends - Your own AI service or inference engine
Service Integration Patterns
Architecture Overview
┌─────────────────────────────────────┐
│ SfAIAssistView Component │
│ (User Interaction & UI) │
└────────────────┬────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ PromptRequested Event Handler │
│ (Orchestration Logic) │
└────────────────┬────────────────────┘
│
┌───────┴───────┬──────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ OpenAI │ │ Azure │ │ Ollama │
│ API │ │ OpenAI │ │ Local │
└────────┘ └────────┘ └────────┘Common Integration Workflow
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
try
{
// 1. Validate input
if (string.IsNullOrWhiteSpace(args.Prompt))
{
args.Response = "<div class='error'>Please enter a prompt</div>";
return;
}
// 2. Show loading state (optional)
args.Response = "<div class='loading'>Processing your request...</div>";
// 3. Call AI service
var response = await CallAIService(args.Prompt);
// 4. Update response
args.Response = response;
}
catch (Exception ex)
{
args.Response = $"<div class='error'>Error: {ex.Message}</div>";
LogError(ex);
}
}
private async Task<string> CallAIService(string prompt)
{
// Implementation varies by service
return "AI response";
}
private void LogError(Exception ex)
{
Console.WriteLine($"AI Service Error: {ex}");
// Log to your monitoring system
}---
OpenAI Integration
Setup
1. Obtain API Key:
- Visit platform.openai.com
- Create or retrieve your API key
- Store securely in
appsettings.jsonor environment variables
2. Configure in Program.cs:
// Program.cs
var openAIApiKey = builder.Configuration["OpenAI:ApiKey"];
builder.Services.AddScoped<IOpenAIService>(sp =>
new OpenAIService(openAIApiKey));3. Add HttpClient:
builder.Services.AddScoped<HttpClient>(sp =>
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {openAIApiKey}");
return client;
});Non-Streaming Implementation
@using Syncfusion.Blazor.InteractiveChat
@using System.Text.Json
@inject HttpClient Http
<div style="height: 400px;">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
try
{
var response = await CallOpenAI(args.Prompt);
args.Response = response;
}
catch (Exception ex)
{
args.Response = $"<div class='error'>Error: {ex.Message}</div>";
}
}
private async Task<string> CallOpenAI(string prompt)
{
var requestBody = new
{
model = "gpt-3.5-turbo",
messages = new[]
{
new { role = "system", content = "You are a helpful assistant." },
new { role = "user", content = prompt }
},
temperature = 0.7,
max_tokens = 1000
};
try
{
var response = await Http.PostAsJsonAsync(
"https://api.openai.com/v1/chat/completions",
requestBody);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(content);
var messageContent = doc.RootElement
.GetProperty("choices")[0]
.GetProperty("message")
.GetProperty("content")
.GetString();
return $"<div>{messageContent}</div>";
}
catch (HttpRequestException ex)
{
return $"<div class='error'>Failed to call OpenAI API: {ex.Message}</div>";
}
}
}Streaming Implementation
@using Syncfusion.Blazor.InteractiveChat
@inject HttpClient Http
<div style="height: 400px;">
<SfAIAssistView
@ref="assistView"
EnableStreaming="true"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private SfAIAssistView? assistView;
private string openAIApiKey = "YOUR_API_KEY"; // Store securely!
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
args.Response = "<div>";
try
{
await StreamOpenAI(args.Prompt);
}
catch (Exception ex)
{
await assistView!.UpdateResponseAsync(
$"<div class='error'>Error: {ex.Message}</div>");
}
await assistView!.UpdateResponseAsync("</div>");
}
private async Task StreamOpenAI(string prompt)
{
var requestBody = new
{
model = "gpt-3.5-turbo",
messages = new[]
{
new { role = "user", content = prompt }
},
stream = true,
temperature = 0.7
};
var request = new HttpRequestMessage(HttpMethod.Post,
"https://api.openai.com/v1/chat/completions")
{
Headers =
{
{ "Authorization", $"Bearer {openAIApiKey}" }
},
Content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json")
};
using var response = await Http.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (line.StartsWith("data: ") && !line.Contains("[DONE]"))
{
var jsonData = line.Substring(6);
var content = ExtractOpenAIContent(jsonData);
if (!string.IsNullOrEmpty(content))
{
await assistView!.UpdateResponseAsync(content);
}
}
}
}
private string ExtractOpenAIContent(string json)
{
try
{
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("choices")[0]
.GetProperty("delta")
.GetProperty("content")
.GetString() ?? "";
}
catch
{
return "";
}
}
}Configuration Options
private class OpenAIConfig
{
public string Model { get; set; } = "gpt-3.5-turbo"; // or "gpt-4"
public float Temperature { get; set; } = 0.7f; // 0-2, higher = more creative
public int MaxTokens { get; set; } = 2000;
public float TopP { get; set; } = 1f; // nucleus sampling
public int FrequencyPenalty { get; set; } = 0;
public int PresencePenalty { get; set; } = 0;
}
// Usage:
var config = new OpenAIConfig { Temperature = 0.5f, Model = "gpt-4" };
await StreamOpenAI(prompt, config);---
Azure OpenAI Integration
Setup
1. Create Azure OpenAI Resource:
- Deploy in Azure Portal
- Note: Deployment name, Endpoint, and API Key
2. Configure:
// appsettings.json
{
"AzureOpenAI": {
"Endpoint": "https://YOUR_RESOURCE.openai.azure.com/",
"ApiKey": "YOUR_API_KEY",
"DeploymentName": "gpt-35-turbo"
}
}Implementation
@using Syncfusion.Blazor.InteractiveChat
@using System.Text.Json
@inject HttpClient Http
@inject IConfiguration Config
<div style="height: 400px;">
<SfAIAssistView
@ref="assistView"
EnableStreaming="true"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private SfAIAssistView? assistView;
private string azureEndpoint = "";
private string azureApiKey = "";
private string deploymentName = "";
protected override void OnInitialized()
{
azureEndpoint = Config["AzureOpenAI:Endpoint"];
azureApiKey = Config["AzureOpenAI:ApiKey"];
deploymentName = Config["AzureOpenAI:DeploymentName"];
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
args.Response = "<div>";
try
{
await StreamAzureOpenAI(args.Prompt);
}
catch (Exception ex)
{
await assistView!.UpdateResponseAsync(
$"<div class='error'>Error: {ex.Message}</div>");
}
await assistView!.UpdateResponseAsync("</div>");
}
private async Task StreamAzureOpenAI(string prompt)
{
var url = $"{azureEndpoint}openai/deployments/{deploymentName}/chat/completions?api-version=2024-02-15-preview";
var requestBody = new
{
messages = new[]
{
new { role = "system", content = "You are a helpful AI assistant." },
new { role = "user", content = prompt }
},
stream = true,
temperature = 0.7
};
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Headers =
{
{ "api-key", azureApiKey }
},
Content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json")
};
using var response = await Http.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (line.StartsWith("data: ") && !line.Contains("[DONE]"))
{
var content = ExtractAzureContent(line.Substring(6));
if (!string.IsNullOrEmpty(content))
{
await assistView!.UpdateResponseAsync(content);
}
}
}
}
private string ExtractAzureContent(string json)
{
try
{
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("choices")[0]
.GetProperty("delta")
.GetProperty("content")
.GetString() ?? "";
}
catch
{
return "";
}
}
}API Version Management
private string GetApiVersionUrl()
{
// Azure OpenAI API versions: 2023-05-15, 2023-06-01-preview, 2024-02-15-preview
return $"api-version=2024-02-15-preview";
}
// Deployment naming conventions
private string GetDeploymentUrl(string resourceName, string deployment)
{
return $"https://{resourceName}.openai.azure.com/openai/deployments/{deployment}/chat/completions";
}---
Local Ollama Integration
Setup
1. Install Ollama:
- Download from ollama.ai
- Run:
ollama serve - Default endpoint:
http://localhost:11434
2. Pull a Model:
ollama pull llama2
ollama pull mistral
ollama pull neural-chat3. Verify Connection:
// Test endpoint availability
var client = new HttpClient();
var response = await client.GetAsync("http://localhost:11434/api/tags");
// If successful, Ollama is runningImplementation
@using Syncfusion.Blazor.InteractiveChat
@using System.Text.Json
@inject HttpClient Http
<div style="height: 400px;">
<SfAIAssistView
@ref="assistView"
EnableStreaming="true"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
<div class="mt-3">
<select @bind="selectedModel">
<option value="llama2">Llama 2</option>
<option value="mistral">Mistral</option>
<option value="neural-chat">Neural Chat</option>
</select>
</div>
@code {
private SfAIAssistView? assistView;
private string ollamaEndpoint = "http://localhost:11434";
private string selectedModel = "llama2";
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
args.Response = "<div>";
try
{
await StreamOllama(args.Prompt);
}
catch (Exception ex)
{
await assistView!.UpdateResponseAsync(
$"<div class='error'>Error: {ex.Message}</div>");
}
await assistView!.UpdateResponseAsync("</div>");
}
private async Task StreamOllama(string prompt)
{
var requestBody = new
{
model = selectedModel,
prompt = prompt,
stream = true,
temperature = 0.7
};
var request = new HttpRequestMessage(HttpMethod.Post,
$"{ollamaEndpoint}/api/generate")
{
Content = new StringContent(
JsonSerializer.Serialize(requestBody),
Encoding.UTF8,
"application/json")
};
using var response = await Http.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Ollama API error: {response.StatusCode}");
}
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
try
{
using var doc = JsonDocument.Parse(line);
var responseText = doc.RootElement
.GetProperty("response")
.GetString();
if (!string.IsNullOrEmpty(responseText))
{
await assistView!.UpdateResponseAsync(responseText);
}
// Check if generation is complete
if (doc.RootElement.GetProperty("done").GetBoolean())
{
break;
}
}
catch (JsonException)
{
// Skip malformed lines
}
}
}
}
private async Task<List<string>> GetAvailableModels()
{
try
{
var response = await Http.GetAsync($"{ollamaEndpoint}/api/tags");
var content = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(content);
var models = new List<string>();
foreach (var model in doc.RootElement.GetProperty("models").EnumerateArray())
{
models.Add(model.GetProperty("name").GetString()!);
}
return models;
}
catch
{
return new List<string>();
}
}
}Available Models
| Model | Size | Speed | Quality | Notes |
|---|---|---|---|---|
| neural-chat | 4GB | Very Fast | Good | Best for chat |
| mistral | 4GB | Fast | Very Good | Excellent reasoning |
| llama2 | 7GB | Medium | Excellent | Balanced performance |
| llama2:13b | 25GB | Slow | Excellent | High quality responses |
---
Custom Backend Integration
Abstract Service Pattern
// Define interface for any AI service
public interface IAIService
{
Task<string> GetResponseAsync(string prompt);
Task StreamResponseAsync(string prompt, Func<string, Task> onChunkReceived);
}
// Implement for your backend
public class CustomAIService : IAIService
{
private readonly HttpClient _httpClient;
private readonly string _apiEndpoint;
public CustomAIService(HttpClient httpClient, string apiEndpoint)
{
_httpClient = httpClient;
_apiEndpoint = apiEndpoint;
}
public async Task<string> GetResponseAsync(string prompt)
{
var request = new { prompt = prompt };
var response = await _httpClient.PostAsJsonAsync(
$"{_apiEndpoint}/chat", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsAsync<dynamic>();
return result.response;
}
public async Task StreamResponseAsync(string prompt,
Func<string, Task> onChunkReceived)
{
var request = new { prompt = prompt };
var httpRequest = new HttpRequestMessage(HttpMethod.Post,
$"{_apiEndpoint}/chat/stream")
{
Content = new StringContent(
JsonSerializer.Serialize(request),
Encoding.UTF8,
"application/json")
};
using var response = await _httpClient.SendAsync(httpRequest,
HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
await onChunkReceived(line);
}
}
}
}Usage in Component
@using Syncfusion.Blazor.InteractiveChat
@inject IAIService aiService
<div style="height: 400px;">
<SfAIAssistView
@ref="assistView"
EnableStreaming="true"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private SfAIAssistView? assistView;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
args.Response = "<div>";
try
{
await aiService.StreamResponseAsync(args.Prompt, async chunk =>
{
await assistView!.UpdateResponseAsync(chunk);
});
}
catch (Exception ex)
{
await assistView!.UpdateResponseAsync(
$"<div class='error'>Error: {ex.Message}</div>");
}
await assistView!.UpdateResponseAsync("</div>");
}
}---
Error Handling Strategies
Network Error Handling
private async Task<string> CallWithRetry(string prompt, int maxRetries = 3)
{
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
return await CallAIService(prompt);
}
catch (HttpRequestException) when (attempt < maxRetries - 1)
{
// Exponential backoff: 1s, 2s, 4s
await Task.Delay(1000 * (int)Math.Pow(2, attempt));
}
catch (Exception ex)
{
return $"<div class='error'>Failed after {maxRetries} attempts: {ex.Message}</div>";
}
}
return "<div class='error'>Service unavailable</div>";
}Timeout Handling
private async Task<string> CallWithTimeout(string prompt, TimeSpan timeout)
{
using var cts = new CancellationTokenSource(timeout);
try
{
return await CallAIServiceAsync(prompt, cts.Token);
}
catch (OperationCanceledException)
{
return "<div class='error'>Request timed out (> " +
timeout.TotalSeconds + "s)</div>";
}
}Fallback Service Pattern
private async Task<string> CallWithFallback(string prompt)
{
try
{
return await CallPrimaryService(prompt);
}
catch (Exception ex1)
{
Console.WriteLine($"Primary service failed: {ex1.Message}");
try
{
return await CallSecondaryService(prompt);
}
catch (Exception ex2)
{
Console.WriteLine($"Secondary service failed: {ex2.Message}");
return "<div class='error'>All services unavailable</div>";
}
}
}---
Rate Limiting & Throttling
Token Bucket Rate Limiter
public class RateLimiter
{
private DateTime _lastRefillTime = DateTime.Now;
private double _tokensAvailable = 0;
private readonly double _tokensPerSecond;
private readonly double _maxTokens;
public RateLimiter(double tokensPerSecond, double maxTokens)
{
_tokensPerSecond = tokensPerSecond;
_maxTokens = maxTokens;
_tokensAvailable = maxTokens;
}
public async Task WaitIfNeeded(double tokensRequired = 1)
{
RefillTokens();
while (_tokensAvailable < tokensRequired)
{
await Task.Delay(100);
RefillTokens();
}
_tokensAvailable -= tokensRequired;
}
private void RefillTokens()
{
var now = DateTime.Now;
var timePassed = (now - _lastRefillTime).TotalSeconds;
_tokensAvailable = Math.Min(_maxTokens,
_tokensAvailable + timePassed * _tokensPerSecond);
_lastRefillTime = now;
}
}
// Usage:
private RateLimiter limiter = new RateLimiter(tokensPerSecond: 5, maxTokens: 10);
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await limiter.WaitIfNeeded();
var response = await CallAIService(args.Prompt);
args.Response = response;
}Request Queue
public class AIServiceQueue
{
private readonly Queue<Func<Task<string>>> _requestQueue = new();
private bool _isProcessing = false;
public async Task<string> EnqueueRequest(Func<Task<string>> request)
{
var taskCompletionSource = new TaskCompletionSource<string>();
_requestQueue.Enqueue(async () =>
{
try
{
var result = await request();
taskCompletionSource.SetResult(result);
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
if (!_isProcessing)
{
_ = ProcessQueue();
}
return await taskCompletionSource.Task;
}
private async Task ProcessQueue()
{
_isProcessing = true;
while (_requestQueue.Count > 0)
{
var request = _requestQueue.Dequeue();
await request();
await Task.Delay(500); // Rate limit between requests
}
_isProcessing = false;
}
}---
Best Practices
1. Secure API Key Management
// ✓ DO: Use configuration and secrets
var apiKey = builder.Configuration["OpenAI:ApiKey"];
// ✗ DON'T: Hardcode API keys
// var apiKey = "sk-abc123...";
// ✓ DO: Use User Secrets in development
// dotnet user-secrets set "OpenAI:ApiKey" "sk-abc123..."2. Implement Logging
@inject ILogger<AIComponent> Logger
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
Logger.LogInformation("Processing prompt: {Prompt}", args.Prompt);
try
{
var response = await CallAIService(args.Prompt);
args.Response = response;
Logger.LogInformation("Response generated successfully");
}
catch (Exception ex)
{
Logger.LogError(ex, "AI service call failed");
args.Response = "<div class='error'>An error occurred</div>";
}
}3. Handle Streaming Cancellation
private CancellationTokenSource? _streamingCts;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
_streamingCts = new CancellationTokenSource();
try
{
await StreamResponse(args.Prompt, _streamingCts.Token);
}
catch (OperationCanceledException)
{
// User stopped the response
}
}
private void OnResponseStopped(ResponseStoppedEventArgs args)
{
_streamingCts?.Cancel();
}4. Validate Input
private string ValidatePrompt(string prompt)
{
if (string.IsNullOrWhiteSpace(prompt))
return "Prompt cannot be empty";
if (prompt.Length > 10000)
return "Prompt exceeds maximum length (10000 characters)";
return ""; // Valid
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
var validationError = ValidatePrompt(args.Prompt);
if (!string.IsNullOrEmpty(validationError))
{
args.Response = $"<div class='error'>{validationError}</div>";
return;
}
// Process prompt
}5. Monitor Resource Usage
private async Task<string> CallWithResourceMonitoring(string prompt)
{
var startMemory = GC.GetTotalMemory(false);
var startTime = DateTime.Now;
try
{
var response = await CallAIService(prompt);
var endMemory = GC.GetTotalMemory(false);
var duration = DateTime.Now - startTime;
Logger.LogInformation(
"AI call - Duration: {Duration}ms, Memory delta: {Memory}KB",
duration.TotalMilliseconds,
(endMemory - startMemory) / 1024);
return response;
}
catch (Exception ex)
{
Logger.LogError(ex, "AI service failed");
return "<div class='error'>Service error</div>";
}
}6. Test Different Services
#if DEBUG
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Test mode: cycle through services
var service = args.Prompt.StartsWith("azure:") ? "azure" :
args.Prompt.StartsWith("ollama:") ? "ollama" : "openai";
var prompt = args.Prompt.Replace("azure:", "").Replace("ollama:", "");
var response = service switch
{
"azure" => await CallAzureOpenAI(prompt),
"ollama" => await CallOllama(prompt),
_ => await CallOpenAI(prompt)
};
args.Response = response;
}
#endif---
Summary
This guide provides production-ready integration patterns for connecting the AI AssistView component to various AI service backends:
- OpenAI - Cloud-hosted with excellent quality
- Azure OpenAI - Enterprise deployment option
- Ollama - Local hosting for privacy
- Custom - Your own backend service
All patterns include error handling, rate limiting, and best practices for production use. Choose the service that best fits your requirements and customize the implementations as needed.
````markdown
Attachments
Table of Contents
- Enabling Attachments
- Attachment Settings Configuration
- File Type Restrictions
- File Size Limits
- Upload Configuration
- Attachment Events
- Handling Attachment Clicks
- Custom Upload Logic
- Best Practices
Enabling Attachments
The AI AssistView component supports file attachments, allowing users to upload documents, images, and other files alongside their prompts. Enable attachments using the AttachmentSettings property:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px; width: 650px;">
<SfAIAssistView PromptRequested="@OnPromptRequested">
<AssistViewAttachmentSettings
Enable="true"
SaveUrl="api/upload"
RemoveUrl="api/remove">
</AssistViewAttachmentSettings>
</SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Access attachments
var attachments = args.Attachments;
await Task.Delay(1000);
args.Response = $"<div>Received {attachments?.Count ?? 0} attachment(s)</div>";
}
}Attachment Settings Configuration
Configure attachment behavior with AssistViewAttachmentSettings:
<SfAIAssistView PromptRequested="@OnPromptRequested">
<AssistViewAttachmentSettings
Enable="true"
AllowedFileTypes=".jpg,.png,.pdf,.docx"
MaxFileSize="5000000"
MaximumCount="5"
SaveUrl="api/attachments/upload"
RemoveUrl="api/attachments/remove">
</AssistViewAttachmentSettings>
</SfAIAssistView>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Processing your request with attachments...</div>";
}
}AttachmentSettings Properties
| Property | Type | Default | Description |
|---|---|---|---|
Enable | bool | false | Enables or disables attachment feature |
AllowedFileTypes | string | "" | Comma-separated file extensions (e.g., ".jpg,.png,.pdf") |
MaxFileSize | double | 2000000 | Maximum file size in bytes (default: 2MB) |
MaximumCount | int | 10 | Maximum number of files per message |
SaveUrl | string | "" | Server endpoint for file uploads |
RemoveUrl | string | "" | Server endpoint for file deletions |
File Type Restrictions
Restrict uploadable file types using the AllowedFileTypes property:
<!-- Images only -->
<AssistViewAttachmentSettings
Enable="true"
AllowedFileTypes=".jpg,.jpeg,.png,.gif,.webp"
SaveUrl="api/upload">
</AssistViewAttachmentSettings>
<!-- Documents only -->
<AssistViewAttachmentSettings
Enable="true"
AllowedFileTypes=".pdf,.docx,.xlsx,.txt"
SaveUrl="api/upload">
</AssistViewAttachmentSettings>
<!-- Mixed types -->
<AssistViewAttachmentSettings
Enable="true"
AllowedFileTypes=".jpg,.png,.pdf,.docx,.txt,.zip"
SaveUrl="api/upload">
</AssistViewAttachmentSettings>File Size Limits
Control maximum file size with MaxFileSize (in bytes):
<!-- 1 MB limit -->
<AssistViewAttachmentSettings
Enable="true"
MaxFileSize="1000000"
SaveUrl="api/upload">
</AssistViewAttachmentSettings>
<!-- 5 MB limit -->
<AssistViewAttachmentSettings
Enable="true"
MaxFileSize="5000000"
SaveUrl="api/upload">
</AssistViewAttachmentSettings>
<!-- 10 MB limit -->
<AssistViewAttachmentSettings
Enable="true"
MaxFileSize="10000000"
SaveUrl="api/upload">
</AssistViewAttachmentSettings>Size Calculation:
- 1 MB = 1,000,000 bytes
- 5 MB = 5,000,000 bytes
- 10 MB = 10,000,000 bytes
Upload Configuration
Server-Side Upload Endpoint
Create an API controller to handle file uploads:
[ApiController]
[Route("api/attachments")]
public class AttachmentsController : ControllerBase
{
private readonly IWebHostEnvironment _environment;
public AttachmentsController(IWebHostEnvironment environment)
{
_environment = environment;
}
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded");
// Validate file size (5MB limit)
if (file.Length > 5000000)
return BadRequest("File size exceeds limit");
// Validate file type
var allowedExtensions = new[] { ".jpg", ".png", ".pdf", ".docx" };
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!allowedExtensions.Contains(extension))
return BadRequest("File type not allowed");
// Save file
var uploadsFolder = Path.Combine(_environment.WebRootPath, "uploads");
Directory.CreateDirectory(uploadsFolder);
var fileName = $"{Guid.NewGuid()}{extension}";
var filePath = Path.Combine(uploadsFolder, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok(new { fileName, filePath });
}
[HttpPost("remove")]
public IActionResult Remove([FromBody] string fileName)
{
if (string.IsNullOrEmpty(fileName))
return BadRequest("No file name provided");
var filePath = Path.Combine(_environment.WebRootPath, "uploads", fileName);
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
return Ok();
}
return NotFound("File not found");
}
}Client-Side Configuration
<SfAIAssistView PromptRequested="@OnPromptRequested">
<AssistViewAttachmentSettings
Enable="true"
SaveUrl="/api/attachments/upload"
RemoveUrl="/api/attachments/remove"
AllowedFileTypes=".jpg,.png,.pdf,.docx"
MaxFileSize="5000000"
MaximumCount="3">
</AssistViewAttachmentSettings>
</SfAIAssistView>Attachment Events
Handle attachment-related events to customize behavior:
@using Syncfusion.Blazor.InteractiveChat
@using Syncfusion.Blazor.Inputs
<SfAIAssistView
PromptRequested="@OnPromptRequested"
OnAttachmentUploadReady="@OnAttachmentUploadReady"
AttachmentUploadChange="@OnAttachmentUploadChange"
AttachmentUploadSuccess="@OnAttachmentUploadSuccess"
AttachmentUploadFailed="@OnAttachmentUploadFailed"
AttachmentRemoved="@OnAttachmentRemoved"
AttachmentClick="@OnAttachmentClick">
<AssistViewAttachmentSettings
Enable="true"
SaveUrl="api/attachments/upload"
RemoveUrl="api/attachments/remove">
</AssistViewAttachmentSettings>
</SfAIAssistView>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response with attachments processed</div>";
}
private void OnAttachmentUploadReady(AttachmentUploadReadyEventArgs args)
{
// Called before upload begins
Console.WriteLine($"Preparing to upload {args.FilesData.Length} file(s)");
// Add custom form data
args.CustomFormData = new Dictionary<string, object>
{
{ "userId", "user123" },
{ "category", "documents" }
};
// Cancel upload if needed
// args.Cancel = true;
}
private void OnAttachmentUploadChange(UploadChangeEventArgs args)
{
// Called when files are selected/changed
Console.WriteLine($"Files changed: {args.Files.Count}");
}
private void OnAttachmentUploadSuccess(SuccessEventArgs args)
{
// Called on successful upload
Console.WriteLine($"Upload successful: {args.File.Name}");
}
private void OnAttachmentUploadFailed(FailureEventArgs args)
{
// Called on upload failure
Console.WriteLine($"Upload failed: {args.File.Name} - {args.Response.StatusText}");
}
private void OnAttachmentRemoved(RemovingEventArgs args)
{
// Called when attachment is removed
Console.WriteLine($"Attachment removed: {args.FilesData[0].Name}");
}
private void OnAttachmentClick(AttachmentClickEventArgs args)
{
// Called when attachment is clicked
Console.WriteLine($"Attachment clicked: {args.SelectedFile.Name}");
}
}Handling Attachment Clicks
Implement custom behavior when users click on attachments:
@using Syncfusion.Blazor.InteractiveChat
@inject IJSRuntime JS
<SfAIAssistView
PromptRequested="@OnPromptRequested"
AttachmentClick="@OnAttachmentClick">
<AssistViewAttachmentSettings
Enable="true"
SaveUrl="api/attachments/upload">
</AssistViewAttachmentSettings>
</SfAIAssistView>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Processing your request...</div>";
}
private async Task OnAttachmentClick(AttachmentClickEventArgs args)
{
var file = args.SelectedFile;
// Show file details
Console.WriteLine($"File: {file.Name}");
Console.WriteLine($"Size: {file.Size} bytes");
Console.WriteLine($"Type: {file.Type}");
// Option 1: Open file in new window
await JS.InvokeVoidAsync("window.open", $"/uploads/{file.Name}", "_blank");
// Option 2: Download file
// await JS.InvokeVoidAsync("downloadFile", file.Name);
// Option 3: Show preview modal
// await ShowFilePreview(file);
}
}Custom Upload Logic
Implement custom upload logic using the OnAttachmentUploadReady event:
@using Syncfusion.Blazor.InteractiveChat
@inject HttpClient Http
<SfAIAssistView
PromptRequested="@OnPromptRequested"
OnAttachmentUploadReady="@OnAttachmentUploadReady">
<AssistViewAttachmentSettings
Enable="true"
SaveUrl="api/attachments/upload">
</AssistViewAttachmentSettings>
</SfAIAssistView>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
private void OnAttachmentUploadReady(AttachmentUploadReadyEventArgs args)
{
// Add authentication token
args.CurrentRequest = new Dictionary<string, object>
{
{ "Authorization", "Bearer YOUR_TOKEN_HERE" }
};
// Add custom metadata
args.CustomFormData = new Dictionary<string, object>
{
{ "userId", GetCurrentUserId() },
{ "timestamp", DateTime.UtcNow.ToString("o") },
{ "sessionId", Guid.NewGuid().ToString() }
};
// Validate file before upload
foreach (var file in args.FilesData)
{
if (file.Size > 5000000)
{
args.Cancel = true;
Console.WriteLine($"File {file.Name} exceeds size limit");
return;
}
// Check file content (example)
if (IsVirusDetected(file))
{
args.Cancel = true;
Console.WriteLine($"File {file.Name} failed security check");
return;
}
}
}
private string GetCurrentUserId()
{
// Return current user ID
return "user123";
}
private bool IsVirusDetected(FileInfo file)
{
// Implement virus scanning logic
return false;
}
}Best Practices
1. File Validation
Always validate files on both client and server:
private void OnAttachmentUploadReady(AttachmentUploadReadyEventArgs args)
{
var allowedExtensions = new[] { ".jpg", ".png", ".pdf", ".docx" };
foreach (var file in args.FilesData)
{
var extension = Path.GetExtension(file.Name).ToLowerInvariant();
if (!allowedExtensions.Contains(extension))
{
args.Cancel = true;
ShowError($"File type {extension} not allowed");
return;
}
if (file.Size > 5000000)
{
args.Cancel = true;
ShowError($"File {file.Name} exceeds 5MB limit");
return;
}
}
}2. Security Considerations
- Validate file types on server, not just client
- Scan for malware before saving files
- Store files outside web root when possible
- Use unique filenames to prevent overwrites
- Implement rate limiting to prevent abuse
3. Error Handling
private void OnAttachmentUploadFailed(FailureEventArgs args)
{
var errorMessage = args.Response.StatusText;
var fileName = args.File.Name;
// Log error
Logger.LogError($"Upload failed for {fileName}: {errorMessage}");
// Show user-friendly message
if (args.Response.StatusCode == 413)
{
ShowError("File is too large. Maximum size is 5MB.");
}
else if (args.Response.StatusCode == 415)
{
ShowError("File type not supported.");
}
else
{
ShowError("Upload failed. Please try again.");
}
}4. Progress Indication
Provide feedback during upload:
private string uploadStatus = "";
private void OnAttachmentUploadReady(AttachmentUploadReadyEventArgs args)
{
uploadStatus = $"Uploading {args.FilesData.Length} file(s)...";
StateHasChanged();
}
private void OnAttachmentUploadSuccess(SuccessEventArgs args)
{
uploadStatus = $"Successfully uploaded {args.File.Name}";
StateHasChanged();
}5. Cleanup
Remove temporary files when no longer needed:
private void OnAttachmentRemoved(RemovingEventArgs args)
{
// Call server to delete file
foreach (var file in args.FilesData)
{
_ = DeleteFileFromServer(file.Name);
}
}
private async Task DeleteFileFromServer(string fileName)
{
try
{
await Http.DeleteAsync($"api/attachments/{fileName}");
}
catch (Exception ex)
{
Logger.LogError($"Failed to delete {fileName}: {ex.Message}");
}
}6. Accessibility
Ensure attachment functionality is accessible:
<AssistViewAttachmentSettings
Enable="true"
SaveUrl="api/upload"
AllowedFileTypes=".jpg,.png,.pdf"
MaxFileSize="5000000">
</AssistViewAttachmentSettings>
<!-- Add descriptive text for screen readers -->
<div class="sr-only">
Supported file types: JPG, PNG, PDF. Maximum file size: 5MB.
</div>Complete Example
@page "/ai-assistant-with-attachments"
@using Syncfusion.Blazor.InteractiveChat
@using Syncfusion.Blazor.Inputs
<div style="height: 500px; width: 700px;">
<SfAIAssistView
PromptRequested="@OnPromptRequested"
OnAttachmentUploadReady="@OnAttachmentUploadReady"
AttachmentUploadSuccess="@OnAttachmentUploadSuccess"
AttachmentUploadFailed="@OnAttachmentUploadFailed"
AttachmentClick="@OnAttachmentClick">
<AssistViewAttachmentSettings
Enable="true"
AllowedFileTypes=".jpg,.png,.pdf,.docx"
MaxFileSize="5000000"
MaximumCount="3"
SaveUrl="/api/attachments/upload"
RemoveUrl="/api/attachments/remove">
</AssistViewAttachmentSettings>
</SfAIAssistView>
</div>
@if (!string.IsNullOrEmpty(statusMessage))
{
<div class="alert alert-info mt-2">@statusMessage</div>
}
@code {
private string statusMessage = "";
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
var attachmentCount = args.Attachments?.Count ?? 0;
await Task.Delay(1000);
if (attachmentCount > 0)
{
args.Response = $"<div><strong>Processing your request with {attachmentCount} attachment(s)...</strong></div>";
}
else
{
args.Response = "<div>Response to your prompt</div>";
}
}
private void OnAttachmentUploadReady(AttachmentUploadReadyEventArgs args)
{
statusMessage = $"Uploading {args.FilesData.Length} file(s)...";
// Add custom headers or metadata
args.CustomFormData = new Dictionary<string, object>
{
{ "userId", "user123" },
{ "timestamp", DateTime.UtcNow }
};
}
private void OnAttachmentUploadSuccess(SuccessEventArgs args)
{
statusMessage = $"Successfully uploaded: {args.File.Name}";
}
private void OnAttachmentUploadFailed(FailureEventArgs args)
{
statusMessage = $"Upload failed: {args.File.Name} - {args.Response.StatusText}";
}
private void OnAttachmentClick(AttachmentClickEventArgs args)
{
statusMessage = $"Clicked attachment: {args.SelectedFile.Name}";
}
}````
Customization & Icons
Table of Contents
- Prompt Icon Customization
- Response Icon Customization
- Using Built-in Icon Libraries
- CSS Styling
- Theme Integration
Prompt Icon Customization
The PromptIconCss property customizes the appearance of the user avatar that displays next to prompt messages:
@using Syncfusion.Blazor.InteractiveChat
<div class="aiassist-container" style="height: 350px; width: 650px;">
<SfAIAssistView
Prompts="@prompts"
PromptRequested="@OnPromptRequested"
PromptIconCss="e-icons e-user">
</SfAIAssistView>
</div>
@code {
private List<AssistViewPrompt> prompts = new()
{
new AssistViewPrompt()
{
Prompt = "What is AI?",
Response = "<div>AI stands for Artificial Intelligence...</div>"
}
};
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Available Prompt Icon Classes:
e-icons e-user- Generic user icone-icons e-person- Person silhouettee-icons e-profile- Profile icone-icons e-user-circle- Circular user icon- Custom CSS classes for brand-specific icons
Response Icon Customization
The ResponseIconCss property customizes the AI avatar that appears next to response messages. The default is e-assistview-icon:
@using Syncfusion.Blazor.InteractiveChat
<div class="aiassist-container" style="height: 350px; width: 650px;">
<SfAIAssistView
Prompts="@prompts"
PromptRequested="@OnPromptRequested"
ResponseIconCss="e-icons e-bullet-2">
</SfAIAssistView>
</div>
@code {
private List<AssistViewPrompt> prompts = new()
{
new AssistViewPrompt()
{
Prompt = "What is AI?",
Response = "<div>AI stands for Artificial Intelligence...</div>"
}
};
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Available Response Icon Classes:
e-icons e-bullet-2- Filled bullet pointe-icons e-robots- Robot/AI icone-icons e-bot- Bot icon (if available)e-icons e-settings- Settings/gear icone-assistview-icon- Default Syncfusion AI icon (recommended)
Using Built-in Icon Libraries
Syncfusion Built-in Icons
<!-- Show user with default user icon -->
<SfAIAssistView
PromptIconCss="e-icons e-user"
ResponseIconCss="e-assistview-icon"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
<!-- Show user and AI with robot/bot icons -->
<SfAIAssistView
PromptIconCss="e-icons e-person"
ResponseIconCss="e-icons e-robots"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Font Awesome Icons
If using Font Awesome in your project:
<!-- Include Font Awesome in App.razor -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet" /><!-- Use Font Awesome classes -->
<SfAIAssistView
PromptIconCss="fas fa-user"
ResponseIconCss="fas fa-robot"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Bootstrap Icons
If using Bootstrap Icons in your project:
<!-- Include Bootstrap Icons in App.razor -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet" /><!-- Use Bootstrap Icons classes -->
<SfAIAssistView
PromptIconCss="bi bi-person-circle"
ResponseIconCss="bi bi-robot"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>CSS Styling
Component Container Styling
@page "/assistant"
<style>
.aiassist-container {
height: 400px;
width: 100%;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.custom-chat {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 12px;
}
</style>
<div class="custom-chat">
<div class="aiassist-container">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Message Styling
Customize how prompts and responses are displayed:
@page "/assistant"
<style>
/* Style user prompts */
.e-assistview-prompt {
background-color: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 12px;
margin: 8px 0;
border-radius: 4px;
}
/* Style AI responses */
.e-assistview-response {
background-color: #f5f5f5;
border-left: 4px solid #4caf50;
padding: 12px;
margin: 8px 0;
border-radius: 4px;
}
/* Style icons */
.e-assistview-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
</style>
<div style="height: 400px;">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Theme Integration
Syncfusion Theme Consistency
Ensure the component uses the same theme as your application:
@page "/assistant"
<!-- Import theme stylesheet -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<div style="height: 400px;">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Available Themes:
bootstrap5.css- Bootstrap 5 themematerial.css- Material Designfluent.css- Fluent UI (Microsoft)tailwind.css- Tailwind CSSfabric.css- Office Fabricmaterial-dark.css- Dark mode
Custom Theme Styling
Create a custom theme by overriding Syncfusion classes:
@page "/assistant"
<style>
/* Override Syncfusion theme colors */
.e-assistview {
--primary-color: #6366f1;
--response-bg: #f8f9fa;
--prompt-bg: #e0e7ff;
}
/* Custom dark theme */
.dark-theme .e-assistview {
--response-bg: #2a2a2a;
--prompt-bg: #404040;
color: #e0e0e0;
}
</style>
<div style="height: 400px;" class="dark-theme">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Complete Customization Example
@page "/custom-assistant"
@using Syncfusion.Blazor.InteractiveChat
<style>
.custom-container {
height: 500px;
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.chat-wrapper {
height: 100%;
background: white;
border-radius: 8px;
display: flex;
flex-direction: column;
}
.chat-header {
padding: 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 8px 8px 0 0;
font-weight: bold;
}
.chat-content {
flex: 1;
overflow-y: auto;
}
</style>
<div class="custom-container">
<div class="chat-wrapper">
<div class="chat-header">
<span class="bi bi-robot"></span>
AI Assistant
</div>
<div class="chat-content">
<SfAIAssistView
PromptIconCss="bi bi-person-circle"
ResponseIconCss="bi bi-robot"
PromptSuggestions="@suggestions"
PromptSuggestionsHeader="Quick Questions"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
</div>
</div>
@code {
private List<string> suggestions = new()
{
"What can you help me with?",
"How do I get started?",
"Show me an example"
};
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = $"<div><strong>Response:</strong> {args.Prompt}</div>";
}
}Best Practices
1. Choose theme-consistent icons - match your application's design language 2. Use semantic icons - select icons that clearly represent their purpose 3. Consider accessibility - provide appropriate ARIA labels and descriptions 4. Test icon visibility - ensure icons are visible on all themes 5. Maintain visual consistency - use the same icon style throughout the app 6. Responsive design - adjust container size for different screen sizes
Getting Started with AI AssistView
Installation
NuGet Package Setup
Install the Syncfusion.Blazor.InteractiveChat NuGet package:
dotnet add package Syncfusion.Blazor.InteractiveChatImport Namespaces
Add the required using statements to your _Imports.razor file:
@using Syncfusion.Blazor
@using Syncfusion.Blazor.InteractiveChatRegister Service
In your Program.cs, add the Syncfusion Blazor service registration:
// Program.cs
builder.Services.AddSyncfusionBlazor();Add Theme
Include the Syncfusion Blazor theme stylesheet in your App.razor or layout file:
<!-- Bootstrap 5 theme (or choose another: material, fluent, tailwind) -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<!-- Syncfusion Blazor script -->
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"></script>Available themes: bootstrap5.css, material.css, fluent.css, tailwind.css, fabric.css, material-dark.css
Basic Component Structure
Minimal Example
Create a simple AI AssistView component with prompt handling:
@page "/ai-assistant"
@using Syncfusion.Blazor.InteractiveChat
<h2>AI Assistant</h2>
<div class="aiassist-container" style="height: 400px; width: 650px;">
<SfAIAssistView
Prompt="How can I help you today?"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Simulate AI response delay
await Task.Delay(1000);
// Set response text
args.Response = "<div>This is a response from the AI.</div>";
}
}Component Properties
- Prompt: Sets the initial prompt text displayed in the conversation
- PromptRequested: Event triggered when the user submits a prompt; use this to call your AI service
PromptRequested Event Handler
The PromptRequested event provides access to the user's input through AssistViewPromptRequestedEventArgs:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// args.Prompt contains the user's input text
string userPrompt = args.Prompt;
// Call your AI service (OpenAI, Azure, Ollama, etc.)
var aiResponse = await CallAIService(userPrompt);
// Set the response - can be plain text or HTML
args.Response = aiResponse;
}
private async Task<string> CallAIService(string prompt)
{
// Example: Call OpenAI API
// var client = new OpenAIClient(apiKey);
// var response = await client.Chat.Completions.CreateAsync(...);
// return response.Choices[0].Message.Content;
return $"<div><strong>AI Response:</strong> Processing '{prompt}'...</div>";
}Async Response Processing
The component supports asynchronous prompt processing, allowing you to make API calls before returning responses:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
try
{
// Add processing delay to simulate real API call
await Task.Delay(2000);
// Example: Call external AI service
var response = await FetchAIResponse(args.Prompt);
args.Response = response;
}
catch (Exception ex)
{
args.Response = $"<div class='error'>Error: {ex.Message}</div>";
}
}
private async Task<string> FetchAIResponse(string prompt)
{
// Simulated API call
// In production, integrate with OpenAI, Azure OpenAI, Ollama, etc.
return $"<div>Response to: {prompt}</div>";
}Component Sizing
Control component dimensions using Width and Height properties:
<!-- Fixed dimensions -->
<SfAIAssistView
Width="600px"
Height="400px"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
<!-- Percentage-based sizing -->
<SfAIAssistView
Width="100%"
Height="500px"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
<!-- Using container styling -->
<div style="height: 400px; width: 100%;">
<SfAIAssistView PromptRequested="@OnPromptRequested"></SfAIAssistView>
</div>Additional Properties
Show/Hide Header
Control header visibility with ShowHeader:
<!-- Hide header for embedded scenarios -->
<SfAIAssistView
ShowHeader="false"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Custom CSS Classes
Apply custom styling with CssClass:
<style>
.my-custom-assistant {
border: 2px solid #0066cc;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
</style>
<SfAIAssistView
CssClass="my-custom-assistant"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Right-to-Left Support
Enable RTL layout for right-to-left languages:
<SfAIAssistView
EnableRtl="true"
PromptPlaceholder="اكتب سؤالك هنا..."
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Component ID
Set a specific ID for the component:
<SfAIAssistView
ID="aiAssistant1"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>Common Configuration
Basic setup with common properties:
@using Syncfusion.Blazor.InteractiveChat
<SfAIAssistView
Prompt="Welcome to AI Assistant"
PromptPlaceholder="Type your question..."
PromptRequested="@OnPromptRequested"
EnableScrollToBottom="true">
</SfAIAssistView>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = $"<div>You asked: {args.Prompt}</div>";
}
}Component Lifecycle Events
Handle component creation with the Created event:
<SfAIAssistView
Created="@OnCreated"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
@code {
private void OnCreated(object args)
{
Console.WriteLine("AI AssistView component created");
// Initialize component state
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}Next Steps
- Prompt Suggestions: Add guided suggestions to help users refine queries
- Customization: Change avatar icons and styling
- Advanced Features: Enable markdown rendering and manage conversation history
- AI Integration: Connect to OpenAI, Azure, or your custom API
- Attachments: Enable file uploads with attachment settings
- Multi-View: Create specialized views for different purposes
- Streaming: Implement real-time streaming responses
- Toolbars: Add custom actions with toolbars
- Templates: Fully customize the UI with templates
````markdown
Methods and Programmatic API
Table of Contents
- Overview
- ExecutePromptAsync
- UpdateResponseAsync
- RefreshUIAsync
- ScrollToBottomAsync
- Common Usage Patterns
- Best Practices
Overview
The AI AssistView component provides several methods for programmatic control, allowing you to trigger prompts, update responses, refresh the UI, and control scrolling behavior without direct user interaction.
Available Methods:
ExecutePromptAsync(string)- Execute a prompt programmaticallyUpdateResponseAsync(string)- Update streaming response contentRefreshUIAsync()- Refresh component UI and stateScrollToBottomAsync()- Scroll to bottom of conversation
ExecutePromptAsync
Execute prompts programmatically without user input.
Basic Usage
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 400px;">
<SfAIAssistView
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
<div class="mt-3">
<button class="btn btn-primary" @onclick="ExecuteWelcomePrompt">
Show Welcome Message
</button>
<button class="btn btn-secondary" @onclick="ExecuteHelpPrompt">
Show Help
</button>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = args.PromptText switch
{
"welcome" => "<div><h4>Welcome!</h4><p>This is an AI assistant. How can I help you?</p></div>",
"help" => "<div><h4>Help</h4><ul><li>Ask questions</li><li>Get assistance</li><li>Learn more</li></ul></div>",
_ => $"<div>Processing: {args.PromptText}</div>"
};
}
private async Task ExecuteWelcomePrompt()
{
if (assistView != null)
{
await assistView.ExecutePromptAsync("welcome");
}
}
private async Task ExecuteHelpPrompt()
{
if (assistView != null)
{
await assistView.ExecutePromptAsync("help");
}
}
}Auto-Execute on Initialize
Execute a prompt when the component loads:
@code {
private SfAIAssistView? assistView;
private bool hasExecutedWelcome = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !hasExecutedWelcome && assistView != null)
{
hasExecutedWelcome = true;
await Task.Delay(500); // Small delay for component initialization
await assistView.ExecutePromptAsync("Welcome! How can I assist you today?");
}
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Thank you for using the AI Assistant!</div>";
}
}Automated Workflows
Execute a series of prompts automatically:
@code {
private SfAIAssistView? assistView;
private async Task RunAutomatedDemo()
{
if (assistView == null) return;
var demoPrompts = new[]
{
"What is Blazor?",
"How do I create a component?",
"Show me an example"
};
foreach (var prompt in demoPrompts)
{
await assistView.ExecutePromptAsync(prompt);
await Task.Delay(3000); // Wait 3 seconds between prompts
}
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = $"<div>Demo response for: {args.PromptText}</div>";
}
}
<button class="btn btn-info" @onclick="RunAutomatedDemo">
Run Demo
</button>Context-Based Auto-Prompts
Execute prompts based on application context:
@code {
private SfAIAssistView? assistView;
private string currentPage = "";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
currentPage = GetCurrentPage();
await ExecuteContextualPrompt();
}
}
private async Task ExecuteContextualPrompt()
{
if (assistView == null) return;
var contextPrompt = currentPage switch
{
"dashboard" => "Show me key metrics and insights",
"settings" => "What settings can I configure?",
"profile" => "How do I update my profile?",
_ => "How can I help you?"
};
await assistView.ExecutePromptAsync(contextPrompt);
}
private string GetCurrentPage()
{
// Get current page from navigation
return "dashboard";
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = $"<div>Context-specific response for: {args.PromptText}</div>";
}
}UpdateResponseAsync
Update streaming response content progressively (see streaming.md for detailed examples).
Basic Update
@code {
private SfAIAssistView? assistView;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
args.Response = "<div>Starting response...";
// Update response incrementally
await Task.Delay(500);
await assistView!.UpdateResponseAsync(" Adding more content...");
await Task.Delay(500);
await assistView.UpdateResponseAsync(" Final content.</div>");
}
}RefreshUIAsync
Refresh the component UI and footer state manually.
Basic Refresh
@code {
private SfAIAssistView? assistView;
private List<AssistViewPrompt> prompts = new();
private async Task AddPromptAndRefresh()
{
// Modify prompts collection
prompts.Add(new AssistViewPrompt
{
Prompt = "New prompt",
Response = "<div>New response</div>"
});
// Refresh UI to show changes
if (assistView != null)
{
await assistView.RefreshUIAsync();
}
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response content</div>";
}
}
<button class="btn btn-primary" @onclick="AddPromptAndRefresh">
Add Prompt & Refresh
</button>Refresh After External Changes
Refresh UI when data changes outside the component:
@using System.Timers
@implements IDisposable
@code {
private SfAIAssistView? assistView;
private List<AssistViewPrompt> prompts = new();
private Timer? refreshTimer;
protected override void OnInitialized()
{
// Set up periodic refresh
refreshTimer = new Timer(5000); // Every 5 seconds
refreshTimer.Elapsed += async (sender, e) => await RefreshFromServer();
refreshTimer.Start();
}
private async Task RefreshFromServer()
{
// Fetch updated data from server
var newPrompts = await FetchPromptsFromServer();
if (newPrompts.Count != prompts.Count)
{
prompts = newPrompts;
if (assistView != null)
{
await assistView.RefreshUIAsync();
StateHasChanged();
}
}
}
private async Task<List<AssistViewPrompt>> FetchPromptsFromServer()
{
// Simulate server fetch
await Task.Delay(100);
return prompts;
}
public void Dispose()
{
refreshTimer?.Stop();
refreshTimer?.Dispose();
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response</div>";
}
}Refresh After Bulk Operations
Refresh UI after adding multiple items:
@code {
private SfAIAssistView? assistView;
private List<AssistViewPrompt> prompts = new();
private async Task ImportConversation()
{
// Add multiple prompts
var importedPrompts = new[]
{
new AssistViewPrompt { Prompt = "Q1", Response = "<div>A1</div>" },
new AssistViewPrompt { Prompt = "Q2", Response = "<div>A2</div>" },
new AssistViewPrompt { Prompt = "Q3", Response = "<div>A3</div>" }
};
prompts.AddRange(importedPrompts);
// Refresh once after all additions
if (assistView != null)
{
await assistView.RefreshUIAsync();
}
}
}ScrollToBottomAsync
Scroll the conversation to the bottom programmatically.
Basic Scroll
@code {
private SfAIAssistView? assistView;
private async Task ScrollToLatestMessage()
{
if (assistView != null)
{
await assistView.ScrollToBottomAsync();
}
}
}
<button class="btn btn-secondary" @onclick="ScrollToLatestMessage">
Scroll to Bottom
</button>Auto-Scroll After Loading History
Scroll to bottom after loading conversation history:
@code {
private SfAIAssistView? assistView;
private List<AssistViewPrompt> prompts = new();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LoadConversationHistory();
}
}
private async Task LoadConversationHistory()
{
// Load prompts from storage
prompts = await GetSavedConversation();
// Wait for UI to render
await Task.Delay(100);
// Scroll to bottom to show latest messages
if (assistView != null)
{
await assistView.ScrollToBottomAsync();
}
}
private async Task<List<AssistViewPrompt>> GetSavedConversation()
{
// Fetch from storage
await Task.Delay(500);
return new List<AssistViewPrompt>
{
new AssistViewPrompt { Prompt = "Old Q1", Response = "<div>Old A1</div>" },
new AssistViewPrompt { Prompt = "Old Q2", Response = "<div>Old A2</div>" }
};
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>Response</div>";
}
}Scroll on New Message
Automatically scroll when new messages arrive:
@code {
private SfAIAssistView? assistView;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "<div>New response content</div>";
// Scroll to show the new response
await Task.Delay(100); // Wait for render
if (assistView != null)
{
await assistView.ScrollToBottomAsync();
}
}
}Common Usage Patterns
Pattern 1: Guided Tour
Create an interactive guided tour:
@code {
private SfAIAssistView? assistView;
private int tourStep = 0;
private string[] tourPrompts = new[]
{
"Welcome to the AI Assistant tour!",
"You can ask me questions about any topic",
"Try typing a question in the input box",
"You can also use the suggested prompts",
"That's it! Enjoy using the AI Assistant"
};
private async Task StartTour()
{
for (tourStep = 0; tourStep < tourPrompts.Length; tourStep++)
{
if (assistView != null)
{
await assistView.ExecutePromptAsync(tourPrompts[tourStep]);
await Task.Delay(4000); // Wait 4 seconds between steps
}
}
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(500);
args.Response = $"<div>Tour step {tourStep + 1} of {tourPrompts.Length}</div>";
}
}
<button class="btn btn-success" @onclick="StartTour">
Start Guided Tour
</button>Pattern 2: Error Recovery
Automatically retry failed operations:
@code {
private SfAIAssistView? assistView;
private int retryCount = 0;
private const int MaxRetries = 3;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
try
{
retryCount = 0;
var response = await CallAIServiceWithRetry(args.PromptText);
args.Response = response;
}
catch (Exception ex)
{
args.Response = $"<div class='alert alert-danger'>Failed after {MaxRetries} retries: {ex.Message}</div>";
}
}
private async Task<string> CallAIServiceWithRetry(string prompt)
{
while (retryCount < MaxRetries)
{
try
{
return await CallAIService(prompt);
}
catch (HttpRequestException) when (retryCount < MaxRetries - 1)
{
retryCount++;
await Task.Delay(1000 * retryCount); // Exponential backoff
// Show retry message
if (assistView != null)
{
await assistView.RefreshUIAsync();
}
}
}
throw new Exception("Maximum retries exceeded");
}
private async Task<string> CallAIService(string prompt)
{
// Simulate AI service call
await Task.Delay(1000);
return $"<div>Response to: {prompt}</div>";
}
}Pattern 3: Batch Processing
Process multiple prompts in batch:
@code {
private SfAIAssistView? assistView;
private List<string> batchPrompts = new();
private async Task ProcessBatch()
{
batchPrompts = new List<string>
{
"Analyze dataset A",
"Generate report for Q1",
"Summarize findings"
};
foreach (var prompt in batchPrompts)
{
if (assistView != null)
{
await assistView.ExecutePromptAsync(prompt);
await Task.Delay(2000); // Wait between prompts
}
}
// Scroll to bottom after batch
if (assistView != null)
{
await assistView.ScrollToBottomAsync();
}
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
var index = batchPrompts.IndexOf(args.PromptText) + 1;
args.Response = $"<div>Batch response {index}/{batchPrompts.Count}</div>";
}
}
<button class="btn btn-primary" @onclick="ProcessBatch">
Process Batch
</button>Pattern 4: Real-Time Updates
Update conversation based on external events:
@using Microsoft.AspNetCore.SignalR.Client
@implements IAsyncDisposable
@code {
private SfAIAssistView? assistView;
private HubConnection? hubConnection;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:5001/chathub")
.Build();
hubConnection.On<string>("ReceiveMessage", async (message) =>
{
if (assistView != null)
{
await assistView.ExecutePromptAsync(message);
}
});
await hubConnection.StartAsync();
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = $"<div>Real-time response: {args.PromptText}</div>";
}
public async ValueTask DisposeAsync()
{
if (hubConnection != null)
{
await hubConnection.DisposeAsync();
}
}
}Best Practices
1. Always Check for Null
@code {
private async Task SafeExecutePrompt(string prompt)
{
if (assistView != null)
{
await assistView.ExecutePromptAsync(prompt);
}
}
}2. Handle Component Lifecycle
@code {
private SfAIAssistView? assistView;
private bool isComponentReady = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
isComponentReady = true;
await ExecuteInitialPrompts();
}
}
private async Task ExecuteInitialPrompts()
{
if (isComponentReady && assistView != null)
{
await assistView.ExecutePromptAsync("Welcome!");
}
}
}3. Use Try-Catch for Error Handling
@code {
private async Task SafeMethodCall()
{
try
{
if (assistView != null)
{
await assistView.ExecutePromptAsync("test");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
// Show error to user
}
}
}4. Debounce Rapid Calls
@code {
private DateTime lastRefreshTime = DateTime.MinValue;
private readonly TimeSpan refreshDebounceInterval = TimeSpan.FromMilliseconds(500);
private async Task DebouncedRefresh()
{
var now = DateTime.Now;
if (now - lastRefreshTime > refreshDebounceInterval)
{
lastRefreshTime = now;
if (assistView != null)
{
await assistView.RefreshUIAsync();
}
}
}
}5. Provide User Feedback
@code {
private string statusMessage = "";
private bool isProcessing = false;
private async Task ExecuteWithFeedback(string prompt)
{
isProcessing = true;
statusMessage = "Processing...";
StateHasChanged();
try
{
if (assistView != null)
{
await assistView.ExecutePromptAsync(prompt);
statusMessage = "Complete!";
}
}
catch (Exception ex)
{
statusMessage = $"Error: {ex.Message}";
}
finally
{
isProcessing = false;
StateHasChanged();
}
}
}Summary
The programmatic API provides powerful control over the AI AssistView component:
- ExecutePromptAsync - Automate prompts and create workflows
- UpdateResponseAsync - Implement streaming responses
- RefreshUIAsync - Sync UI with external data changes
- ScrollToBottomAsync - Ensure latest content is visible
Use these methods to create sophisticated AI-powered experiences that respond to user actions, external events, and application state changes.
````
````markdown
Multi-View Support
Table of Contents
- Overview
- Creating Multiple Views
- Active View Management
- View Configuration
- View Switching
- View-Specific Settings
- Common Patterns
Overview
The AI AssistView component supports multiple views, allowing you to create specialized AI assistants for different purposes within the same component. Each view can have its own header, icon, banner, templates, and behavior.
Use Cases:
- Different AI personalities (e.g., "Code Assistant", "Writing Helper", "Data Analyst")
- Domain-specific assistants (e.g., "Sales", "Support", "Marketing")
- Language-specific assistants
- Task-specific workflows
Creating Multiple Views
Use the AssistViews collection to define multiple assist views:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px; width: 700px;">
<SfAIAssistView
PromptRequested="@OnPromptRequested"
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged">
<AssistViews>
<AssistView Header="Code Assistant" IconCss="e-icons e-code">
</AssistView>
<AssistView Header="Writing Helper" IconCss="e-icons e-edit">
</AssistView>
<AssistView Header="Data Analyst" IconCss="e-icons e-chart">
</AssistView>
</AssistViews>
</SfAIAssistView>
</div>
@code {
private int activeViewIndex = 0;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
// Respond based on active view
var response = activeViewIndex switch
{
0 => $"<div><strong>[Code Assistant]</strong> {GenerateCodeResponse(args.PromptText)}</div>",
1 => $"<div><strong>[Writing Helper]</strong> {GenerateWritingResponse(args.PromptText)}</div>",
2 => $"<div><strong>[Data Analyst]</strong> {GenerateDataResponse(args.PromptText)}</div>",
_ => "<div>Response</div>"
};
args.Response = response;
}
private void OnActiveViewChanged(int newIndex)
{
activeViewIndex = newIndex;
Console.WriteLine($"Switched to view: {newIndex}");
}
private string GenerateCodeResponse(string prompt)
{
return "Here's a code solution...";
}
private string GenerateWritingResponse(string prompt)
{
return "Here's a writing suggestion...";
}
private string GenerateDataResponse(string prompt)
{
return "Here's a data analysis...";
}
}Active View Management
Getting and Setting Active View
Control which view is currently displayed using the ActiveView property:
@using Syncfusion.Blazor.InteractiveChat
<div class="view-switcher mb-3">
<button class="btn btn-primary" @onclick="() => SwitchToView(0)">Code Assistant</button>
<button class="btn btn-primary" @onclick="() => SwitchToView(1)">Writing Helper</button>
<button class="btn btn-primary" @onclick="() => SwitchToView(2)">Data Analyst</button>
</div>
<div style="height: 500px;">
<SfAIAssistView
@bind-ActiveView="activeViewIndex"
PromptRequested="@OnPromptRequested">
<AssistViews>
<AssistView Header="Code Assistant" IconCss="e-icons e-code"></AssistView>
<AssistView Header="Writing Helper" IconCss="e-icons e-edit"></AssistView>
<AssistView Header="Data Analyst" IconCss="e-icons e-chart"></AssistView>
</AssistViews>
</SfAIAssistView>
</div>
@code {
private int activeViewIndex = 0;
private void SwitchToView(int index)
{
activeViewIndex = index;
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = $"<div>Response from view {activeViewIndex}</div>";
}
}Listening to View Changes
React to view changes using the ActiveViewChanged event:
@code {
private int activeViewIndex = 0;
private string currentViewName = "Code Assistant";
private void OnActiveViewChanged(int newIndex)
{
activeViewIndex = newIndex;
currentViewName = newIndex switch
{
0 => "Code Assistant",
1 => "Writing Helper",
2 => "Data Analyst",
_ => "Unknown"
};
// Load view-specific data
LoadViewData(newIndex);
StateHasChanged();
}
private void LoadViewData(int viewIndex)
{
// Load conversation history for this view
// Reset prompts or load saved state
Console.WriteLine($"Loading data for view: {viewIndex}");
}
}View Configuration
Configuring Individual Views
Each AssistView can have its own configuration:
<SfAIAssistView PromptRequested="@OnPromptRequested">
<AssistViews>
<!-- Code Assistant View -->
<AssistView
Header="Code Assistant"
IconCss="e-icons e-code"
ShowClearButton="true">
</AssistView>
<!-- Writing Helper View -->
<AssistView
Header="Writing Helper"
IconCss="e-icons e-edit"
ShowClearButton="false">
</AssistView>
<!-- Support Assistant View -->
<AssistView
Header="Customer Support"
IconCss="e-icons e-help"
ShowClearButton="true">
</AssistView>
</AssistViews>
</SfAIAssistView>View Properties
| Property | Type | Default | Description |
|---|---|---|---|
Header | string | "AI Assist" | View name displayed in header/tabs |
IconCss | string | "" | CSS class for view icon |
ShowClearButton | bool | false | Show clear button in prompt area |
BannerTemplate | RenderFragment | null | Custom template for initial banner |
FooterTemplate | RenderFragment | null | Custom template for footer |
ViewTemplate | RenderFragment | null | Custom template for entire view |
PromptItemTemplate | RenderFragment | null | Custom template for prompts |
ResponseItemTemplate | RenderFragment | null | Custom template for responses |
View Switching
Programmatic View Switching
Switch views programmatically based on conditions:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px;">
<SfAIAssistView
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged"
PromptRequested="@OnPromptRequested">
<AssistViews>
<AssistView Header="General" IconCss="e-icons e-comment"></AssistView>
<AssistView Header="Code" IconCss="e-icons e-code"></AssistView>
<AssistView Header="Math" IconCss="e-icons e-calculator"></AssistView>
</AssistViews>
</SfAIAssistView>
</div>
@code {
private int activeViewIndex = 0;
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Auto-switch views based on prompt content
var prompt = args.PromptText.ToLower();
if (prompt.Contains("code") || prompt.Contains("function") || prompt.Contains("class"))
{
activeViewIndex = 1; // Switch to Code view
}
else if (prompt.Contains("calculate") || prompt.Contains("math") || prompt.Contains("equation"))
{
activeViewIndex = 2; // Switch to Math view
}
else
{
activeViewIndex = 0; // Stay in General view
}
await Task.Delay(1000);
args.Response = GenerateViewSpecificResponse(args.PromptText, activeViewIndex);
}
private void OnActiveViewChanged(int newIndex)
{
activeViewIndex = newIndex;
}
private string GenerateViewSpecificResponse(string prompt, int viewIndex)
{
return $"<div>Response from view {viewIndex} for: {prompt}</div>";
}
}View Navigation UI
Create custom navigation for views:
@page "/multi-view-assistant"
@using Syncfusion.Blazor.InteractiveChat
<style>
.view-tabs {
display: flex;
gap: 8px;
margin-bottom: 16px;
border-bottom: 2px solid #ddd;
}
.view-tab {
padding: 12px 24px;
border: none;
background: transparent;
cursor: pointer;
font-weight: 500;
border-bottom: 3px solid transparent;
}
.view-tab.active {
color: #0066cc;
border-bottom-color: #0066cc;
}
.view-tab:hover {
background: #f0f0f0;
}
</style>
<div class="view-tabs">
@for (int i = 0; i < viewConfigs.Count; i++)
{
var index = i;
var config = viewConfigs[i];
<button
class="view-tab @(activeViewIndex == index ? "active" : "")"
@onclick="() => SwitchView(index)">
<i class="@config.IconCss"></i> @config.Name
</button>
}
</div>
<div style="height: 500px;">
<SfAIAssistView
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged"
PromptRequested="@OnPromptRequested"
ShowHeader="false">
<AssistViews>
@foreach (var config in viewConfigs)
{
<AssistView Header="@config.Name" IconCss="@config.IconCss"></AssistView>
}
</AssistViews>
</SfAIAssistView>
</div>
@code {
private int activeViewIndex = 0;
private List<ViewConfig> viewConfigs = new()
{
new ViewConfig { Name = "General", IconCss = "e-icons e-comment" },
new ViewConfig { Name = "Code", IconCss = "e-icons e-code" },
new ViewConfig { Name = "Writing", IconCss = "e-icons e-edit" },
new ViewConfig { Name = "Data", IconCss = "e-icons e-chart" }
};
private void SwitchView(int index)
{
activeViewIndex = index;
}
private void OnActiveViewChanged(int newIndex)
{
activeViewIndex = newIndex;
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
var viewName = viewConfigs[activeViewIndex].Name;
args.Response = $"<div><strong>[{viewName}]</strong> Processing your request...</div>";
}
public class ViewConfig
{
public string Name { get; set; } = "";
public string IconCss { get; set; } = "";
}
}View-Specific Settings
Separate Prompts per View
Maintain different conversation histories for each view:
@using Syncfusion.Blazor.InteractiveChat
<div style="height: 500px;">
<SfAIAssistView
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged"
Prompts="@GetPromptsForCurrentView()"
PromptRequested="@OnPromptRequested">
<AssistViews>
<AssistView Header="Support" IconCss="e-icons e-help"></AssistView>
<AssistView Header="Sales" IconCss="e-icons e-shopping-cart"></AssistView>
<AssistView Header="Technical" IconCss="e-icons e-settings"></AssistView>
</AssistViews>
</SfAIAssistView>
</div>
@code {
private int activeViewIndex = 0;
private Dictionary<int, List<AssistViewPrompt>> viewPrompts = new()
{
{ 0, new List<AssistViewPrompt>() }, // Support
{ 1, new List<AssistViewPrompt>() }, // Sales
{ 2, new List<AssistViewPrompt>() } // Technical
};
private List<AssistViewPrompt> GetPromptsForCurrentView()
{
return viewPrompts[activeViewIndex];
}
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
var response = GenerateViewSpecificResponse(args.PromptText, activeViewIndex);
args.Response = response;
// Add to view-specific history
viewPrompts[activeViewIndex].Add(new AssistViewPrompt
{
Prompt = args.PromptText,
Response = response
});
}
private void OnActiveViewChanged(int newIndex)
{
activeViewIndex = newIndex;
StateHasChanged();
}
private string GenerateViewSpecificResponse(string prompt, int viewIndex)
{
return viewIndex switch
{
0 => $"<div><strong>Support:</strong> How can we help you with {prompt}?</div>",
1 => $"<div><strong>Sales:</strong> Let me tell you about {prompt}...</div>",
2 => $"<div><strong>Technical:</strong> Here's a technical analysis of {prompt}...</div>",
_ => "<div>Response</div>"
};
}
}View-Specific Suggestions
Provide different suggestions for each view:
@code {
private int activeViewIndex = 0;
private Dictionary<int, List<string>> viewSuggestions = new()
{
{
0, new List<string> // Support
{
"How do I reset my password?",
"Where can I find documentation?",
"How do I contact support?"
}
},
{
1, new List<string> // Sales
{
"What are your pricing plans?",
"Do you offer enterprise solutions?",
"What's included in the premium tier?"
}
},
{
2, new List<string> // Technical
{
"How do I integrate the API?",
"What are the system requirements?",
"How do I deploy to production?"
}
}
};
private List<string> GetSuggestionsForCurrentView()
{
return viewSuggestions.ContainsKey(activeViewIndex)
? viewSuggestions[activeViewIndex]
: new List<string>();
}
}
<SfAIAssistView
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged"
PromptSuggestions="@GetSuggestionsForCurrentView()"
PromptRequested="@OnPromptRequested">
<AssistViews>
<AssistView Header="Support" IconCss="e-icons e-help"></AssistView>
<AssistView Header="Sales" IconCss="e-icons e-shopping-cart"></AssistView>
<AssistView Header="Technical" IconCss="e-icons e-settings"></AssistView>
</AssistViews>
</SfAIAssistView>Common Patterns
Pattern 1: Workspace-Based Views
Create views based on user workspaces or projects:
@code {
private int activeViewIndex = 0;
private List<Workspace> workspaces = new();
protected override async Task OnInitializedAsync()
{
workspaces = await LoadUserWorkspaces();
}
private async Task<List<Workspace>> LoadUserWorkspaces()
{
// Load from database
return new List<Workspace>
{
new Workspace { Id = 1, Name = "Project A", IconCss = "e-icons e-folder" },
new Workspace { Id = 2, Name = "Project B", IconCss = "e-icons e-folder" },
new Workspace { Id = 3, Name = "Project C", IconCss = "e-icons e-folder" }
};
}
public class Workspace
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string IconCss { get; set; } = "";
}
}
<SfAIAssistView
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged"
PromptRequested="@OnPromptRequested">
<AssistViews>
@foreach (var workspace in workspaces)
{
<AssistView Header="@workspace.Name" IconCss="@workspace.IconCss"></AssistView>
}
</AssistViews>
</SfAIAssistView>Pattern 2: Role-Based Views
Different views for different user roles:
@code {
private int activeViewIndex = 0;
private string userRole = ""; // "admin", "developer", "user"
protected override void OnInitialized()
{
userRole = GetCurrentUserRole();
activeViewIndex = GetDefaultViewForRole(userRole);
}
private string GetCurrentUserRole()
{
// Get from auth system
return "developer";
}
private int GetDefaultViewForRole(string role)
{
return role switch
{
"admin" => 0,
"developer" => 1,
"user" => 2,
_ => 0
};
}
}
<SfAIAssistView
ActiveView="@activeViewIndex"
PromptRequested="@OnPromptRequested">
<AssistViews>
<AssistView Header="Admin Console" IconCss="e-icons e-settings"></AssistView>
<AssistView Header="Developer Tools" IconCss="e-icons e-code"></AssistView>
<AssistView Header="User Help" IconCss="e-icons e-help"></AssistView>
</AssistViews>
</SfAIAssistView>Pattern 3: Language-Based Views
Different views for different languages:
@code {
private int activeViewIndex = 0;
private Dictionary<int, string> viewLanguages = new()
{
{ 0, "en" },
{ 1, "es" },
{ 2, "fr" },
{ 3, "de" }
};
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
var language = viewLanguages[activeViewIndex];
var response = await TranslateAndRespond(args.PromptText, language);
args.Response = response;
}
private async Task<string> TranslateAndRespond(string prompt, string language)
{
// Call translation API and generate response
return $"<div>[{language.ToUpper()}] Response to: {prompt}</div>";
}
}
<SfAIAssistView
ActiveView="@activeViewIndex"
ActiveViewChanged="@OnActiveViewChanged"
PromptRequested="@OnPromptRequested">
<AssistViews>
<AssistView Header="English" IconCss="e-icons e-comment"></AssistView>
<AssistView Header="Español" IconCss="e-icons e-comment"></AssistView>
<AssistView Header="Français" IconCss="e-icons e-comment"></AssistView>
<AssistView Header="Deutsch" IconCss="e-icons e-comment"></AssistView>
</AssistViews>
</SfAIAssistView>Best Practices
1. Keep view count reasonable - 3-5 views is optimal for usability 2. Provide clear view names - users should understand each view's purpose 3. Use consistent icons - help users quickly identify views 4. Maintain separate state - each view should have its own conversation history 5. Save view preference - remember which view the user was using 6. Validate view index - ensure activeViewIndex is within bounds 7. Load view data lazily - only load data when view becomes active 8. Provide view descriptions - help users understand what each view does
````
Prompt Configuration
Table of Contents
Setting Prompt Text
Use the Prompt property to define the initial prompt text that appears in the component:
@using Syncfusion.Blazor.InteractiveChat
<div class="aiassist-container" style="height: 350px; width: 650px;">
<SfAIAssistView
Prompt="What tools can help me prioritize tasks?"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
var response = "For real-time prompt processing, connect the AI AssistView component " +
"to your preferred AI service, such as OpenAI or Azure Cognitive Services. " +
"Ensure you obtain the necessary API credentials to authenticate and enable " +
"seamless integration.";
args.Response = response;
}
}Use Cases:
- Display a greeting or instruction as the first prompt
- Initialize the component with a pre-loaded question
- Show example usage patterns to users
Prompt Placeholder
The PromptPlaceholder property sets the placeholder text shown in the prompt input textarea. The default value is "Type prompt for assistance...".
@using Syncfusion.Blazor.InteractiveChat
<div class="aiassist-container" style="height: 350px; width: 650px;">
<SfAIAssistView
PromptPlaceholder="Type a message..."
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
args.Response = "Response to your query";
}
}Common Placeholder Examples:
"Ask me anything...""Describe your issue...""Enter your question...""Type your request here...""How can I assist you?"
Prompt-Response Collection
Use the Prompts property to initialize the component with a collection of existing prompt-response pairs. This represents the conversation history:
@using Syncfusion.Blazor.InteractiveChat
<div class="aiassist-container" style="height: 350px; width: 650px;">
<SfAIAssistView
Prompts="@conversationHistory"
PromptRequested="@OnPromptRequested">
</SfAIAssistView>
</div>
@code {
private List<AssistViewPrompt> conversationHistory = new()
{
new AssistViewPrompt()
{
Prompt = "What is AI?",
Response = "<div>AI stands for Artificial Intelligence, enabling machines to mimic human " +
"intelligence for tasks such as learning, problem-solving, and decision-making.</div>"
},
new AssistViewPrompt()
{
Prompt = "How is AI used?",
Response = "<div>AI is used in recommendation systems, natural language processing, " +
"computer vision, and autonomous systems.</div>"
}
};
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
await Task.Delay(1000);
// Look up response in history or generate new one
var existingPrompt = conversationHistory
.FirstOrDefault(p => p.Prompt == args.Prompt);
if (existingPrompt != null)
{
args.Response = existingPrompt.Response;
}
else
{
args.Response = "For real-time prompt processing, connect to your preferred AI service.";
}
}
}Conversation History Management:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// Generate or fetch response from AI service
var response = await CallAIService(args.Prompt);
// Add new prompt-response pair to history
conversationHistory.Add(new AssistViewPrompt
{
Prompt = args.Prompt,
Response = response
});
args.Response = response;
// Trigger UI refresh if using StateHasChanged
StateHasChanged();
}Event Handling
PromptRequested Event
The PromptRequested event fires when a user submits a prompt. Use this event to integrate with AI services:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
// args.Prompt: User's input text
// Set args.Response to provide the AI's response
var userInput = args.Prompt;
// Call AI service with error handling
try
{
var aiResponse = await CallAIService(userInput);
args.Response = aiResponse;
}
catch (Exception ex)
{
args.Response = $"<div class='error'>An error occurred: {ex.Message}</div>";
}
}Async Processing Pattern
Handle long-running operations gracefully:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
try
{
// Optional: Show loading indicator
args.Response = "<div class='loading'>Processing your request...</div>";
// Simulate API delay
await Task.Delay(2000);
// Call your AI service
var response = await GetAIResponse(args.Prompt);
// Update response with actual content
args.Response = response;
}
catch (HttpRequestException)
{
args.Response = "<div class='error'>Failed to connect to AI service. Please try again.</div>";
}
catch (Exception ex)
{
args.Response = $"<div class='error'>Error: {ex.Message}</div>";
}
}
private async Task<string> GetAIResponse(string prompt)
{
// Replace with actual AI service integration
return $"<div>Processing: {prompt}</div>";
}Response Management
Setting Response Content
Responses can be plain text or HTML:
// Plain text response
args.Response = "This is a simple text response";
// HTML response with formatting
args.Response = "<div>" +
"<strong>Key Point:</strong> This is important<br/>" +
"<em>Additional info:</em> More details here" +
"</div>";
// Response with styling
args.Response = "<div style='color: blue; font-size: 14px;'>" +
"Formatted response content" +
"</div>";Response with Timeout Handling
Implement timeout logic for API calls:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
try
{
var response = await GetAIResponseWithTimeout(args.Prompt, cts.Token);
args.Response = response;
}
catch (OperationCanceledException)
{
args.Response = "<div class='error'>Request timed out. Please try again.</div>";
}
}
}
private async Task<string> GetAIResponseWithTimeout(string prompt, CancellationToken cancellationToken)
{
// Your AI service call here
await Task.Delay(1000, cancellationToken);
return $"Response to: {prompt}";
}Response with Fallback
Provide fallback responses when AI service is unavailable:
private async Task OnPromptRequested(AssistViewPromptRequestedEventArgs args)
{
var response = await CallAIServiceWithFallback(args.Prompt);
args.Response = response;
}
private async Task<string> CallAIServiceWithFallback(string prompt)
{
try
{
// Try primary AI service
return await CallOpenAI(prompt);
}
catch
{
try
{
// Fallback to secondary service
return await CallAzureOpenAI(prompt);
}
catch
{
// Ultimate fallback
return "<div>Unable to process request. Please try again later.</div>";
}
}
}Best Practices
1. Always set `args.Response` in the PromptRequested event handler - never leave it null 2. Handle exceptions gracefully - users should see error messages, not crashes 3. Manage conversation history - save prompts and responses for context 4. Use HTML for rich formatting - responses support HTML content 5. Add delays in testing - simulate real API response times (500ms-2000ms) 6. Cache responses - avoid repeated API calls for identical prompts