Skip to content

Commit

Permalink
Merge pull request #8 from edgett/rag
Browse files Browse the repository at this point in the history
New features:

Upload docs.
Chat with docs.
Persist settings to local storage.
Code improvements.
  • Loading branch information
edgett authored Dec 13, 2023
2 parents e98f02c + 88c7f6f commit 4adff1b
Show file tree
Hide file tree
Showing 69 changed files with 2,801 additions and 745 deletions.
Binary file added Documentation/constitution-rag_test.pdf
Binary file not shown.
24 changes: 24 additions & 0 deletions PalmHill.BlazorChat.ApiClient/BlazorChatApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using PalmHill.BlazorChat.ApiClient.WebApiInterface;
using Refit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PalmHill.BlazorChat.ApiClient
{
public class BlazorChatApi
{
public BlazorChatApi(HttpClient httpClient)
{
HttpClient = httpClient;
Attachment = RestService.For<IAttachment>(httpClient);
Chat = RestService.For<IChat>(httpClient);
}

public HttpClient HttpClient { get; }
public IAttachment Attachment { get; }
public IChat Chat { get; }
}
}
17 changes: 17 additions & 0 deletions PalmHill.BlazorChat.ApiClient/PalmHill.BlazorChat.ApiClient.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Refit" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PalmHill.BlazorChat\Shared\PalmHill.BlazorChat.Shared.csproj" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions PalmHill.BlazorChat.ApiClient/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# PalmHill.BlazorChat.ApiClient

This project is a .NET 8.0 library that provides an API client for the PalmHill Blazor Chat application. It uses the Refit library for HTTP communication.

## Dependencies
- .NET 8.0
- Refit 7.0.0

## Project References
- PalmHill.BlazorChat.Shared

## API Interface
The API client provides an interface to interact with the chat API. The interface is defined in the `IChat` interface.

### Methods
- `Chat(InferenceRequest conversation)`: This method sends a chat message to the server and returns the server's response as a string. The chat message is encapsulated in an `InferenceRequest` object.
- `Ask(InferenceRequest chatConversation)`: This method sends a chat message to the server and returns the server's response as a `ChatMessage` object. The chat message is encapsulated in an `InferenceRequest` object.
- `CancelChat(Guid conversationId)`: This method sends a request to the server to cancel a chat conversation. The ID of the conversation to be cancelled is passed as a parameter.

### BlazorChatApi.cs
The `BlazorChatApi.cs` file contains the `BlazorChatApi` class, which is the main entry point for using the API client. It provides a convenient way to access the interface and its methods.

The `BlazorChatApi` class has a constructor that takes a `HttpClient` object. This `HttpClient` object is used by the Refit library to make HTTP requests.

The `BlazorChatApi` implments the interfaces by using Refit.

## Usage
To use this library, add a reference to it in your project and create an instance of the `IChat` interface using Refit's `RestService` class. Then, you can call the methods defined in the `IChat` interface.

```csharp
var httpClient = new HttpClient { BaseAddress = new Uri("https://api.example.com") };
var blazorChatApi = new BlazorChatApi(httpClient);
var response = await blazorChatApi.Chat.Chat(new InferenceRequest { /* ... */ });
```

Note: This library is not yet available on NuGet.
22 changes: 22 additions & 0 deletions PalmHill.BlazorChat.ApiClient/WebApiInterface/IAttachment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using PalmHill.BlazorChat.Shared.Models;
using Refit;


namespace PalmHill.BlazorChat.ApiClient.WebApiInterface
{
public interface IAttachment
{
[Get("/api/Attachment/list/{conversationId}")]
Task<IEnumerable<AttachmentInfo>> GetAttachments(string conversationId);

[Get("/api/Attachment/{attachmentId}")]
Task<ApiResponse<AttachmentInfo>> GetAttachmentById(string attachmentId);

[Multipart]
[Post("/api/Attachment/{conversationId}/{attachmentId}")]
Task<ApiResponse<AttachmentInfo>> AddAttachment(Guid conversationId, Guid attachmentId, [AliasAs("file")] StreamPart file);

[Delete("/api/Attachment/{attachmentId}")]
Task<ApiResponse<bool>> DeleteAttachment(Guid attachmentId);
}
}
19 changes: 19 additions & 0 deletions PalmHill.BlazorChat.ApiClient/WebApiInterface/IChat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Refit;
using PalmHill.BlazorChat.Shared.Models;


namespace PalmHill.BlazorChat.ApiClient.WebApiInterface
{
public interface IChat
{
[Post("/api/chat")]
Task<ApiResponse<string>> Chat(InferenceRequest conversation);

[Post("/api/chat/docs")]
Task<ApiResponse<ChatMessage>> Ask(InferenceRequest chatConversation);

[Delete("/api/chat/cancel/{conversationId}")]
public Task<ApiResponse<bool>> CancelChat(Guid conversationId);

}
}
12 changes: 12 additions & 0 deletions PalmHill.BlazorChat.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PalmHill.BlazorChat.Server"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PalmHill.BlazorChat.Shared", "PalmHill.BlazorChat\Shared\PalmHill.BlazorChat.Shared.csproj", "{F2337C3B-69E8-43F8-8D21-382C233702D0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PalmHill.LlmMemory", "PalmHill.LlmMemory\PalmHill.LlmMemory.csproj", "{A3075AF8-E520-4B62-8D47-564D9C88E52A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PalmHill.BlazorChat.ApiClient", "PalmHill.BlazorChat.ApiClient\PalmHill.BlazorChat.ApiClient.csproj", "{434FEDF0-2AD0-4276-AC06-E26126EEF237}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -38,6 +42,14 @@ Global
{F2337C3B-69E8-43F8-8D21-382C233702D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2337C3B-69E8-43F8-8D21-382C233702D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2337C3B-69E8-43F8-8D21-382C233702D0}.Release|Any CPU.Build.0 = Release|Any CPU
{A3075AF8-E520-4B62-8D47-564D9C88E52A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3075AF8-E520-4B62-8D47-564D9C88E52A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3075AF8-E520-4B62-8D47-564D9C88E52A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3075AF8-E520-4B62-8D47-564D9C88E52A}.Release|Any CPU.Build.0 = Release|Any CPU
{434FEDF0-2AD0-4276-AC06-E26126EEF237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{434FEDF0-2AD0-4276-AC06-E26126EEF237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{434FEDF0-2AD0-4276-AC06-E26126EEF237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{434FEDF0-2AD0-4276-AC06-E26126EEF237}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
@using PalmHill.BlazorChat.ApiClient
@using PalmHill.BlazorChat.Shared.Models
@inject BlazorChatApi BlazorChatApi

@if (Attachment != null)
{
<FluentPresenceBadge Style="width:100%;" Status="_presenceStatus" StatusTitle="@_presenceLabel">
<FluentCard Style="width:100%; padding:3px 3px" >
<div>
@Attachment.Name
</div>
<FluentButton IconStart="@(new Icons.Regular.Size16.Delete())"
Loading="@_deleteInProgress"
OnClick="@_deleteAttachment"
Title="Delete">
</FluentButton>
</FluentCard>
</FluentPresenceBadge>
}


@code {
/// <summary>
/// The <see cref="AttachmentInfo"/> to display
/// </summary>
[Parameter]
public AttachmentInfo? Attachment { get; set; }

/// <summary>
/// The <see cref="EventCallback"/> to invoke when the attachment is deleted.
/// The event handler will remove the attachment from the list.
/// </summary>
[Parameter]
public EventCallback<AttachmentInfo> OnFileDeleted { get; set; }

private bool _deleteInProgress = false;

/// <summary>
/// Deletes the attachment by calling the api.
/// </summary>
/// <returns></returns>
private async Task _deleteAttachment()
{
if(Attachment == null)
{
return;
}

_deleteInProgress = true;
try
{
var apiResponse = await BlazorChatApi.Attachment.DeleteAttachment(Attachment.Id);

if (!apiResponse.IsSuccessStatusCode )
{
throw new Exception($"Failed to delete attachment. {apiResponse.StatusCode} {apiResponse.ReasonPhrase}");
}

if (!apiResponse.Content)
{
throw new Exception($"Failed to delete attachment. {apiResponse.StatusCode} {apiResponse.ReasonPhrase}");
}

if (apiResponse.IsSuccessStatusCode)
{ //Only invoke when the delete was successful.
//The event handler will remove the attachment from the list.
await OnFileDeleted.InvokeAsync(Attachment);
}

}
catch (Exception ex)
{
Console.WriteLine($"{ex}");
}
_deleteInProgress = false;
}

/// <summary>
/// The <see cref="PresenceStatus"/> to display on the attachment.
/// </summary>
private PresenceStatus _presenceStatus
{
get
{
if (Attachment == null)
{
return PresenceStatus.Busy;

}

var presenceStatus = Attachment.Status switch
{
AttachmentStatus.Pending => PresenceStatus.Away,
AttachmentStatus.Uploaded => PresenceStatus.Available,
AttachmentStatus.Failed => PresenceStatus.Busy,
_ => PresenceStatus.Busy
};

return presenceStatus;
}
}

/// <summary>
/// The tooltip text to display for the attachment.
/// </summary>
private string _presenceLabel
{
get
{
if (Attachment == null)
{
return "";

}

var presenceStatus = Attachment.Status switch
{
AttachmentStatus.Pending => "Pending",
AttachmentStatus.Uploaded => "Ready",
AttachmentStatus.Failed => "Failed",
_ => "Failed"
};

return presenceStatus;
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@if (Controller!.UploadedFiles.Any())
{
<div style="padding:3px 3px;">
@foreach (var file in Controller.UploadedFiles)
{
<AttachmentItemDisplay OnFileDeleted="_fileDeletedHandler" Attachment="file"></AttachmentItemDisplay>
}
</div>
}
@code {
[Parameter]
public ChatService? Controller { get; set; }

/// <summary>
/// Removes the deleted attachment from the <see cref="Controller"/>'s UploadedFiles list.
/// </summary>
/// <param name="deletedAttachment">The deleted <see cref="AttachmentInfo"></param>
private void _fileDeletedHandler(AttachmentInfo deletedAttachment)
{
Controller!.UploadedFiles.Remove(deletedAttachment);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
@using Microsoft.AspNetCore.Components.Forms
@using Refit
@inject BlazorChatApi BlazorChatApi;
<InfoPanel IsOpen="Controller!.AttachmentsVisible" Title="Attachments">
<FluentSwitch

@bind-Value="Controller!.AttachmentsEnabled"
Label="Enable"></FluentSwitch>

<FileInput Controller="Controller" OnInputFileChange="_uploadFiles">
</FileInput>

<AttachmentList Controller="Controller">
</AttachmentList>
</InfoPanel>

@code {

[Parameter]
public ChatService? Controller { get; set; }

/// <summary>
/// Uploads files to the server when the user selects them.
/// Adds to the <see cref="Controller"/>'s UploadedFiles list.
/// </summary>
/// <param name="e">The selected files.</param>
private async Task _uploadFiles(InputFileChangeEventArgs e)
{

var files = e.GetMultipleFiles();
var uploadedCount = 0;
var uploadTasks = new List<Task>();

foreach (var file in files)
{
var attachmentInfo = new AttachmentInfo();
attachmentInfo.ConversationId = Controller?.WebSocketChatConnection?.ConversationId;
attachmentInfo.Name = file.Name;
attachmentInfo.Size = file.Size;
attachmentInfo.ContentType = file.ContentType;
attachmentInfo.Status = AttachmentStatus.Pending;

Controller!.UploadedFiles.Add(attachmentInfo);

var uploadTask = new Task(async () =>
{
if (attachmentInfo?.ConversationId is null)
{
attachmentInfo!.Status = AttachmentStatus.Failed;
return;
}

var stream = file.OpenReadStream(10000000);
var streamPart = new StreamPart(stream, file.Name, file.ContentType);
var apiResponse = await BlazorChatApi.Attachment.AddAttachment(attachmentInfo.ConversationId.Value, attachmentInfo.Id, streamPart);
uploadedCount++;

if (!apiResponse.IsSuccessStatusCode)
{
attachmentInfo.Status = AttachmentStatus.Failed;
}
});

uploadTasks.Add(uploadTask);
}

foreach (var uploadTask in uploadTasks)
{
uploadTask.Start();
await uploadTask;
}
}

}
Loading

0 comments on commit 4adff1b

Please sign in to comment.