Skip to content

Commit a1e9cf9

Browse files
authored
Merge pull request #28 from jongalloway/add-qdrant
🔍 .NET Aspire Demo with Qdrant Search & GitHub Models Integration
2 parents 5c8f175 + 36a4616 commit a1e9cf9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+5187
-85
lines changed

NLWebNet.sln

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.0.31903.59
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
7-
EndProject
86
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet", "src\NLWebNet\NLWebNet.csproj", "{1E458E72-D542-44BB-9F84-1EDE008FBB1D}"
97
EndProject
108
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}"
119
EndProject
1210
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet.Demo", "samples\Demo\NLWebNet.Demo.csproj", "{6F25FD99-AF67-4509-A46C-FCD450F6A775}"
1311
EndProject
14-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet.AspireHost", "samples\AspireHost\NLWebNet.AspireHost.csproj", "{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}"
15-
EndProject
1612
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
1713
EndProject
1814
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet.Tests", "tests\NLWebNet.Tests\NLWebNet.Tests.csproj", "{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}"
@@ -63,25 +59,15 @@ Global
6359
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}.Release|x64.Build.0 = Release|Any CPU
6460
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}.Release|x86.ActiveCfg = Release|Any CPU
6561
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}.Release|x86.Build.0 = Release|Any CPU
66-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
68-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x64.ActiveCfg = Debug|Any CPU
69-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x64.Build.0 = Debug|Any CPU
70-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x86.ActiveCfg = Debug|Any CPU
71-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x86.Build.0 = Debug|Any CPU
72-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
73-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|Any CPU.Build.0 = Release|Any CPU
74-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x64.ActiveCfg = Release|Any CPU
75-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x64.Build.0 = Release|Any CPU
76-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x86.ActiveCfg = Release|Any CPU
77-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x86.Build.0 = Release|Any CPU
7862
EndGlobalSection
7963
GlobalSection(SolutionProperties) = preSolution
8064
HideSolutionNode = FALSE
8165
EndGlobalSection
82-
GlobalSection(NestedProjects) = preSolution {1E458E72-D542-44BB-9F84-1EDE008FBB1D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
66+
GlobalSection(NestedProjects) = preSolution
8367
{6F25FD99-AF67-4509-A46C-FCD450F6A775} = {A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}
8468
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
85-
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB} = {A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}
69+
EndGlobalSection
70+
GlobalSection(ExtensibilityGlobals) = postSolution
71+
SolutionGuid = {8983833A-ABDD-4E53-BEA3-64BCDF1169A5}
8672
EndGlobalSection
8773
EndGlobal

samples/AspireHost/AspireHostingExtensions.cs renamed to samples/AspireDemo/AspireHost/AspireHostingExtensions.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Aspire.Hosting;
2+
using Aspire.Hosting.Qdrant;
23

34
namespace NLWebNet.Extensions;
45

@@ -18,7 +19,7 @@ public static IResourceBuilder<ProjectResource> AddNLWebNetApp(
1819
this IDistributedApplicationBuilder builder,
1920
string name)
2021
{
21-
return builder.AddProject<Projects.NLWebNet_Demo>(name)
22+
return builder.AddProject<Projects.NLWebNet_AspireApp>(name)
2223
.WithEnvironment("ASPNETCORE_ENVIRONMENT", builder.Environment.EnvironmentName)
2324
.WithEnvironment("OTEL_SERVICE_NAME", name)
2425
.WithEnvironment("OTEL_SERVICE_VERSION", "1.0.0");
@@ -56,4 +57,20 @@ public static IResourceBuilder<ProjectResource> AddNLWebNetAppWithDataBackend(
5657
return builder.AddNLWebNetApp(name)
5758
.WithReference(dataBackend);
5859
}
60+
61+
/// <summary>
62+
/// Adds an NLWebNet application with Qdrant vector database reference
63+
/// </summary>
64+
/// <param name="builder">The distributed application builder</param>
65+
/// <param name="name">The name of the application</param>
66+
/// <param name="qdrant">The Qdrant vector database resource to reference</param>
67+
/// <returns>A resource builder for the NLWebNet application</returns>
68+
public static IResourceBuilder<ProjectResource> AddNLWebNetAppWithQdrant(
69+
this IDistributedApplicationBuilder builder,
70+
string name,
71+
IResourceBuilder<QdrantServerResource> qdrant)
72+
{
73+
return builder.AddNLWebNetApp(name)
74+
.WithReference(qdrant);
75+
}
5976
}

samples/AspireHost/NLWebNet.AspireHost.csproj renamed to samples/AspireDemo/AspireHost/NLWebNet.AspireHost.csproj

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,17 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.1.0" />
15-
<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.1.0" />
16-
<PackageReference Include="Aspire.Hosting.Redis" Version="9.1.0" />
17-
<PackageReference Include="Aspire.Hosting.Azure.PostgreSQL" Version="9.1.0" />
18-
<PackageReference Include="Aspire.Hosting.Azure.Redis" Version="9.1.0" />
14+
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.1" />
15+
<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.3.1" />
16+
<PackageReference Include="Aspire.Hosting.Qdrant" Version="9.3.1" />
17+
<PackageReference Include="Aspire.Hosting.Redis" Version="9.3.1" />
18+
<PackageReference Include="Aspire.Hosting.Azure.PostgreSQL" Version="9.3.1" />
19+
<PackageReference Include="Aspire.Hosting.Azure.Redis" Version="9.3.1" />
1920
</ItemGroup>
2021
<ItemGroup>
21-
<ProjectReference Include="..\..\src\NLWebNet\NLWebNet.csproj" IsAspireProjectResource="false" />
22-
<ProjectReference Include="..\Demo\NLWebNet.Demo.csproj" />
22+
<ProjectReference Include="..\NLWebNet.AspireApp\NLWebNet.AspireApp.csproj" />
23+
<ProjectReference Include="..\NLWebNet.Frontend\NLWebNet.Frontend.csproj" />
24+
<ProjectReference Include="..\ServiceDefaults\ServiceDefaults.csproj" IsAspireProjectResource="false" />
2325
</ItemGroup>
2426

2527
</Project>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using NLWebNet.Extensions;
2+
using Microsoft.Extensions.Logging;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
var builder = DistributedApplication.CreateBuilder(args);
6+
7+
// Configure logging to reduce telemetry noise while keeping important startup messages
8+
builder.Services.Configure<LoggerFilterOptions>(options =>
9+
{
10+
// Keep important Aspire startup messages but filter telemetry
11+
options.AddFilter("Aspire.Hosting.ApplicationModel", LogLevel.Information);
12+
options.AddFilter("Aspire.Hosting", LogLevel.Information);
13+
options.AddFilter("Aspire", LogLevel.Warning);
14+
15+
// Reduce OpenTelemetry noise
16+
options.AddFilter("OpenTelemetry", LogLevel.Warning);
17+
18+
// Keep basic hosting messages
19+
options.AddFilter("Microsoft.Extensions.Hosting.Internal.Host", LogLevel.Information);
20+
options.AddFilter("Microsoft.Extensions.Hosting", LogLevel.Warning);
21+
22+
// Reduce ASP.NET Core noise but keep startup messages
23+
options.AddFilter("Microsoft.AspNetCore.Hosting.Diagnostics", LogLevel.Information);
24+
options.AddFilter("Microsoft.AspNetCore", LogLevel.Warning);
25+
26+
// Reduce DI and HTTP noise
27+
options.AddFilter("Microsoft.Extensions.DependencyInjection", LogLevel.Warning);
28+
options.AddFilter("System.Net.Http", LogLevel.Warning);
29+
});
30+
31+
// Add Qdrant vector database for storing ingested data
32+
var qdrant = builder.AddQdrant("qdrant")
33+
.WithDataVolume(); // Persist data between container restarts
34+
35+
// Add external dependencies (optional - could be databases, message queues, etc.)
36+
// var postgres = builder.AddPostgres("postgres")
37+
// .WithEnvironment("POSTGRES_DB", "nlwebnet")
38+
// .PublishAsAzurePostgresFlexibleServer();
39+
40+
// var redis = builder.AddRedis("redis")
41+
// .PublishAsAzureRedis();
42+
43+
// Add the NLWebNet Aspire application with Qdrant integration
44+
var nlwebapp = builder.AddProject<Projects.NLWebNet_AspireApp>("nlwebnet-aspire-api")
45+
.WithEnvironment("ASPNETCORE_ENVIRONMENT", builder.Environment.EnvironmentName)
46+
.WithEnvironment("NLWebNet__RateLimiting__RequestsPerWindow", "1000")
47+
.WithEnvironment("NLWebNet__RateLimiting__WindowSizeInMinutes", "1")
48+
.WithEnvironment("NLWebNet__EnableStreaming", "true")
49+
.WithReference(qdrant) // Connect to Qdrant for vector storage
50+
.WithReplicas(1); // Single replica for demo purposes
51+
52+
// Add the frontend web application
53+
var frontend = builder.AddProject<Projects.NLWebNet_Frontend>("nlwebnet-frontend")
54+
.WithReference(nlwebapp) // Connect to the API
55+
.WithReplicas(1);
56+
57+
var app = builder.Build();
58+
59+
await app.RunAsync();
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# Frontend Integration Summary
2+
3+
## Overview
4+
Successfully integrated GitHub Models AI embeddings with a user-friendly frontend UI for the AspireDemo application. Users can now configure GitHub tokens via the web interface and experience true semantic search.
5+
6+
## Implementation Details
7+
8+
### 1. Configuration Service (`EmbeddingConfigurationService`)
9+
- **Purpose**: Manages GitHub token configuration in the frontend
10+
- **Features**:
11+
- Token validation and storage
12+
- Configuration change events
13+
- Runtime token management
14+
15+
### 2. GitHub Token Input Component (`GitHubTokenInput.razor`)
16+
- **Purpose**: User interface for configuring GitHub Models API access
17+
- **Features**:
18+
- Token input with validation
19+
- Connection testing
20+
- Visual feedback for configuration status
21+
- Help links and instructions
22+
23+
### 3. Configuration Page (`Configuration.razor`)
24+
- **Purpose**: Dedicated page for application configuration
25+
- **Features**:
26+
- GitHub token configuration
27+
- Information about semantic search modes
28+
- Help and documentation links
29+
30+
### 4. API Service (`ApiService`)
31+
- **Purpose**: Frontend service for communicating with the backend API
32+
- **Features**:
33+
- Search requests with optional GitHub token headers
34+
- Health check endpoint calls
35+
- Error handling and logging
36+
37+
### 5. Backend API Updates
38+
- **Enhanced Search Endpoint**: `/api/search`
39+
- Accepts `X-GitHub-Token` header for runtime token configuration
40+
- Uses provided token for GitHub Models API calls
41+
- Falls back to simple embeddings when no token provided
42+
- Returns results compatible with frontend expectations
43+
44+
- **Health Check Endpoint**: `/api/health`
45+
- Simple endpoint for testing API connectivity
46+
- Used by frontend for connection validation
47+
48+
### 6. Embedding Service Extensions
49+
- **Dynamic Token Support**:
50+
- Added overload methods to support runtime GitHub token configuration
51+
- `GenerateEmbeddingAsync(string text, string? githubToken, CancellationToken cancellationToken)`
52+
- Maintains backward compatibility with existing code
53+
54+
## User Experience Flow
55+
56+
### 1. Initial State (No Configuration)
57+
- User sees warning banner on Vector Search page
58+
- Search uses simple fallback embeddings
59+
- Configuration page shows setup instructions
60+
61+
### 2. Token Configuration
62+
- User navigates to Configuration page
63+
- Enters GitHub Personal Access Token
64+
- System validates token format
65+
- Optional connection test verifies API access
66+
67+
### 3. Enhanced Search (With GitHub Models)
68+
- Success banner appears on Vector Search page
69+
- All searches use GitHub Models AI embeddings
70+
- Improved semantic search quality and relevance
71+
72+
### 4. Dynamic Switching
73+
- Users can clear configuration to test differences
74+
- Real-time switching between embedding modes
75+
- Clear visual indicators of current mode
76+
77+
## Technical Architecture
78+
79+
### Frontend (NLWebNet.Frontend)
80+
```
81+
Components/
82+
├── GitHubTokenInput.razor # Token configuration UI
83+
├── Pages/
84+
│ ├── Configuration.razor # Configuration page
85+
│ └── VectorSearch.razor # Updated search with status
86+
└── Services/
87+
├── EmbeddingConfigurationService.cs # Token management
88+
└── ApiService.cs # API communication
89+
```
90+
91+
### Backend (NLWebNet.AspireApp)
92+
```
93+
Services/
94+
├── IEmbeddingService.cs # Extended interface
95+
├── GitHubModelsEmbeddingService.cs # Dynamic token support
96+
└── EmbeddingService.cs # Updated fallback service
97+
98+
Program.cs # Enhanced API endpoints
99+
```
100+
101+
## Configuration Instructions
102+
103+
### For Users
104+
1. Navigate to the **Configuration** page in the app
105+
2. Click the link to create a GitHub Personal Access Token
106+
3. Generate a token with appropriate scopes (public repos require no scopes)
107+
4. Paste the token in the configuration form
108+
5. Test the connection (optional)
109+
6. Navigate to **Vector Search** to use enhanced semantic search
110+
111+
### For Developers
112+
1. Set `GITHUB_TOKEN` environment variable for server-wide configuration
113+
2. Or use the frontend UI for per-session configuration
114+
3. Tokens provided via frontend take precedence over environment variables
115+
116+
## Key Features Demonstrated
117+
118+
### 1. Real Semantic Search
119+
- **GitHub Models**: Uses AI embeddings for true semantic understanding
120+
- **Simple Fallback**: Basic hash-based embeddings for demo purposes
121+
- **Clear Differentiation**: Users can experience the quality difference
122+
123+
### 2. Dynamic Configuration
124+
- **Runtime Token Management**: No need to restart the application
125+
- **Per-Request Tokens**: Frontend can send different tokens per search
126+
- **Fallback Gracefully**: Switches modes seamlessly
127+
128+
### 3. Production-Ready UI
129+
- **Professional Design**: Modern Bootstrap-based interface
130+
- **Comprehensive Help**: Step-by-step configuration guides
131+
- **Visual Feedback**: Clear status indicators and progress feedback
132+
- **Error Handling**: Graceful error messages and recovery
133+
134+
### 4. Developer Experience
135+
- **Clean Abstractions**: Well-defined interfaces and services
136+
- **Extensible Design**: Easy to add new embedding providers
137+
- **Comprehensive Logging**: Detailed logging for debugging
138+
- **Type Safety**: Strong typing throughout the application
139+
140+
## Testing Results
141+
142+
### Build Status
143+
**Solution builds successfully**
144+
- All projects compile without errors
145+
- Dependencies properly resolved
146+
- Type conflicts resolved
147+
148+
### Functional Testing
149+
**Configuration UI works**
150+
- Token input validation functions correctly
151+
- Connection testing validates GitHub API access
152+
- Configuration persistence across page reloads
153+
154+
**API Integration**
155+
- Health check endpoint responds correctly
156+
- Search endpoint accepts token headers
157+
- Embedding service switches modes dynamically
158+
159+
### Expected Behavior
160+
When a user:
161+
1. **Configures GitHub token** → Search quality improves dramatically
162+
2. **Clears configuration** → Falls back to simple embeddings
163+
3. **Tests connection** → Validates API access before searching
164+
4. **Searches with different modes** → Can observe quality differences
165+
166+
## Performance Characteristics
167+
168+
### With GitHub Models
169+
- **Higher Quality**: True semantic understanding
170+
- **Network Dependency**: Requires internet access to GitHub Models API
171+
- **Rate Limits**: Subject to GitHub API rate limiting
172+
- **Latency**: Additional network call for embedding generation
173+
174+
### With Simple Embeddings
175+
- **Lower Quality**: Basic hash-based similarity
176+
- **Local Processing**: No external dependencies
177+
- **Unlimited**: No rate limits or network dependencies
178+
- **Fast Response**: Local computation only
179+
180+
## Security Considerations
181+
182+
### Token Handling
183+
- **Frontend Storage**: Tokens stored in browser session only
184+
- **Header Transmission**: Sent via HTTPS headers to backend
185+
- **No Persistence**: Not stored in databases or logs
186+
- **Scope Minimization**: Recommend minimal GitHub token scopes
187+
188+
### API Security
189+
- **HTTPS Only**: All communication over encrypted channels
190+
- **Header-Based**: Tokens passed in headers, not query parameters
191+
- **Validation**: Token format validation before API calls
192+
- **Error Handling**: No token leakage in error messages
193+
194+
## Future Enhancements
195+
196+
### Potential Improvements
197+
1. **Token Persistence**: Optional browser storage for convenience
198+
2. **Multiple Providers**: Support for OpenAI, Azure OpenAI, etc.
199+
3. **Advanced Configuration**: Model selection, temperature settings
200+
4. **Analytics**: Search quality metrics and usage analytics
201+
5. **Caching**: Embedding caching for improved performance
202+
203+
### Integration Opportunities
204+
1. **User Authentication**: Integrate with GitHub OAuth
205+
2. **Team Management**: Shared token management for organizations
206+
3. **Usage Monitoring**: Track API usage and costs
207+
4. **A/B Testing**: Compare different embedding providers
208+
209+
## Conclusion
210+
211+
The integration successfully demonstrates:
212+
- **Real semantic search** using GitHub Models AI embeddings
213+
- **Professional user interface** for configuration and search
214+
- **Production-ready architecture** with proper error handling
215+
- **Flexible design** supporting multiple embedding providers
216+
- **Clear value proposition** showing the difference between AI and simple embeddings
217+
218+
Users can now experience the full power of semantic vector search with an intuitive interface, while developers have a clean, extensible foundation for further enhancements.

0 commit comments

Comments
 (0)