Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Examples/Examples/Chat/ChatFromExistingExample.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using MaIN.Core.Hub;
using MaIN.Domain.Exceptions;

namespace Examples;

Expand All @@ -18,8 +19,16 @@ public async Task Start()
await result.WithMessage("And about physics?")
.CompleteAsync();

var chatNewContext = await AIHub.Chat().FromExisting(result.GetChatId());
var messages = chatNewContext.GetChatHistory();
Console.WriteLine(JsonSerializer.Serialize(messages));
try
{
var chatNewContext = await AIHub.Chat().FromExisting(result.GetChatId());
var messages = chatNewContext.GetChatHistory();
Console.WriteLine(JsonSerializer.Serialize(messages));
}
catch (ChatNotFoundException ex)
{
Console.WriteLine(ex.PublicErrorMessage);
}

}
}
16 changes: 11 additions & 5 deletions src/MaIN.Core/Hub/Contexts/ChatContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using MaIN.Domain.Configuration;
using MaIN.Domain.Entities;
using MaIN.Domain.Exceptions;
using MaIN.Domain.Models;
using MaIN.Services;
using MaIN.Services.Constants;
Expand Down Expand Up @@ -169,8 +170,9 @@ public async Task<ChatResult> CompleteAsync(
{
if (_chat.Messages.Count == 0)
{
throw new InvalidOperationException("Chat has no messages."); //TODO good candidate for domain exception
throw new EmptyChatException(_chat.Id);
}

_chat.Messages.Last().Files = _files;
if(_preProcess)
{
Expand All @@ -190,7 +192,9 @@ public async Task<ChatResult> CompleteAsync(
public async Task<Chat> GetCurrentChat()
{
if (_chat.Id == null)
throw new InvalidOperationException("Chat has not been created yet. Call CompleteAsync first.");
{
throw new ChatNotInitializedException();
}

return await _chatService.GetById(_chat.Id);
}
Expand All @@ -203,8 +207,10 @@ public async Task<List<Chat>> GetAllChats()
public async Task DeleteChat()
{
if (_chat.Id == null)
throw new InvalidOperationException("Chat has not been created yet.");

{
throw new ChatNotInitializedException();
}

await _chatService.Delete(_chat.Id);
}

Expand All @@ -227,7 +233,7 @@ public async Task<ChatContext> FromExisting(string chatId)
var existingChat = await _chatService.GetById(chatId);
if (existingChat == null)
{
throw new Exception("Chat not found");
throw new ChatNotFoundException(chatId);
}
return new ChatContext(_chatService, existingChat);
}
Expand Down
9 changes: 9 additions & 0 deletions src/MaIN.Domain/Exceptions/AgentContextNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class AgentContextNotFoundException(string agentId) : MaINCustomException($"Context of agent with id: '{agentId}' not found.")
{
public override string PublicErrorMessage => "Agent context not found.";
public override HttpStatusCode HttpStatusCode => HttpStatusCode.NotFound;
}
9 changes: 9 additions & 0 deletions src/MaIN.Domain/Exceptions/AgentFlowNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class AgentFlowNotFoundException(string flowId) : MaINCustomException($"Agent flow with id: '{flowId}' not found.")
{
public override string PublicErrorMessage => "Agent flow not found.";
public override HttpStatusCode HttpStatusCode => HttpStatusCode.NotFound;
}
10 changes: 10 additions & 0 deletions src/MaIN.Domain/Exceptions/AgentNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class AgentNotFoundException(string agentId)
: MaINCustomException($"Agent with id: '{agentId}' not found.")
{
public override string PublicErrorMessage => "Agent not found.";
public override HttpStatusCode HttpStatusCode => HttpStatusCode.NotFound;
}
10 changes: 10 additions & 0 deletions src/MaIN.Domain/Exceptions/ApiRequestFailedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class ApiRequestFailedException(HttpStatusCode statusCode, string requestUrl, string httpMethod)
: MaINCustomException($"API request failed with status code: {statusCode}. Request url: {requestUrl}. Http method: {httpMethod}.")
{
public override string PublicErrorMessage => "An error occurred while processing an external API request";
public override HttpStatusCode HttpStatusCode => HttpStatusCode.InternalServerError;
}
10 changes: 10 additions & 0 deletions src/MaIN.Domain/Exceptions/ChatNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class ChatNotFoundException(string chatId)
: MaINCustomException($"Chat with id: '{chatId}' not found.")
{
public override string PublicErrorMessage => "Chat not found.";
public override HttpStatusCode HttpStatusCode => HttpStatusCode.NotFound;
}
9 changes: 9 additions & 0 deletions src/MaIN.Domain/Exceptions/ChatNotInitializedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class ChatNotInitializedException() : MaINCustomException("Chat has not been created yet. Call 'CompleteAsync' operation first.")
{
public override string PublicErrorMessage => Message;
public override HttpStatusCode HttpStatusCode => HttpStatusCode.Conflict;
}
10 changes: 10 additions & 0 deletions src/MaIN.Domain/Exceptions/CommandFailedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class CommandFailedException(string commandName)
: MaINCustomException($"{commandName} command execution failed.")
{
public override string PublicErrorMessage => Message;
public override HttpStatusCode HttpStatusCode => HttpStatusCode.InternalServerError;
}
9 changes: 9 additions & 0 deletions src/MaIN.Domain/Exceptions/EmptyChatException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class EmptyChatException(string chatId) : MaINCustomException($"Chat with id: '{chatId}' is empty. Complete operation is impossible.")
{
public override string PublicErrorMessage => "Complete operation is impossible, because chat has no message.";
public override HttpStatusCode HttpStatusCode => HttpStatusCode.Conflict;
}
20 changes: 20 additions & 0 deletions src/MaIN.Domain/Exceptions/MaINCustomException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Net;
using System.Text.RegularExpressions;

namespace MaIN.Domain.Exceptions;

public abstract class MaINCustomException(string message) : Exception(message)
{
public string ErrorCode => GenerateErrorCode();
public string LogMessage { get; private set; } = message;
public abstract string PublicErrorMessage { get; }
public abstract HttpStatusCode HttpStatusCode { get; }

private string GenerateErrorCode()
{
var typeName = GetType().Name;
var snakeCaseTypeName = Regex.Replace(typeName, "(?<!^)([A-Z])", "_$1").ToLower();

return snakeCaseTypeName.Replace("_exception", string.Empty);
}
}
10 changes: 10 additions & 0 deletions src/MaIN.Domain/Exceptions/ModelNotDownloadedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class ModelNotDownloadedException(string? modelName)
: MaINCustomException($"Given model {modelName ?? string.Empty} is not downloaded.")
{
public override string PublicErrorMessage => Message;
public override HttpStatusCode HttpStatusCode => HttpStatusCode.NotFound;
}
10 changes: 10 additions & 0 deletions src/MaIN.Domain/Exceptions/ModelNotSupportedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net;

namespace MaIN.Domain.Exceptions;

public class ModelNotSupportedException(string? modelName)
: MaINCustomException($"Given model {modelName ?? string.Empty} is not supported.")
{
public override string PublicErrorMessage => Message;
public override HttpStatusCode HttpStatusCode => HttpStatusCode.BadRequest;
}
15 changes: 7 additions & 8 deletions src/MaIN.Domain/Models/SupportedModels.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using MaIN.Domain.Exceptions;

namespace MaIN.Domain.Models;

public class Model
Expand Down Expand Up @@ -207,25 +209,23 @@ public static Model GetModel(string path, string? name)
StringComparison.InvariantCultureIgnoreCase));
if (model is null)
{
//todo support domain specific exceptions
throw new Exception($"Model {name} is not supported");
throw new ModelNotSupportedException(name);
}

if (File.Exists(Path.Combine(path, model.FileName)))
{
return model;
}

throw new Exception($"Model {name} is not downloaded");
throw new ModelNotDownloadedException(name);
}

public static Model? GetModelByFileName(string path, string fileName)
{
var isPresent = Models.Exists(x => x.FileName == fileName);
if (!isPresent)
{
//todo support domain specific exceptions
Console.WriteLine($"Model {fileName} is not supported");
Console.WriteLine($"{new ModelNotSupportedException(fileName).PublicErrorMessage}");
return null;
}

Expand All @@ -234,7 +234,7 @@ public static Model GetModel(string path, string? name)
return Models.First(x => x.FileName == fileName);
}

throw new Exception($"Model {fileName} is not downloaded");
throw new ModelNotDownloadedException(fileName);
}

public static void AddModel(string model, string path, string? mmProject = null)
Expand All @@ -257,8 +257,7 @@ public static Model GetModel(string modelName)
StringComparison.InvariantCultureIgnoreCase));
if (model is null)
{
//todo support domain specific exceptions
throw new NotSupportedException($"Model {modelName} is not supported");
throw new ModelNotSupportedException(modelName);
}

return model;
Expand Down
7 changes: 5 additions & 2 deletions src/MaIN.Services/Services/AgentFlowService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MaIN.Domain.Entities.Agents.AgentSource;
using MaIN.Domain.Exceptions;
using MaIN.Infrastructure.Models;
using MaIN.Infrastructure.Repositories.Abstract;
using MaIN.Services.Mappers;
Expand All @@ -11,8 +12,10 @@ public class AgentFlowService(IAgentFlowRepository flowRepository, IAgentService
public async Task<AgentFlow> GetFlowById(string id)
{
var flow = await flowRepository.GetFlowById(id);
if(flow is null)
throw new Exception("Flow not found");
if (flow is null)
{
throw new AgentFlowNotFoundException(id);
}

return flow.ToDomain();
}
Expand Down
22 changes: 16 additions & 6 deletions src/MaIN.Services/Services/AgentService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using MaIN.Domain.Configuration;
using MaIN.Domain.Entities;
using MaIN.Domain.Entities.Agents;
using MaIN.Domain.Exceptions;
using MaIN.Infrastructure.Repositories.Abstract;
using MaIN.Services.Constants;
using MaIN.Services.Mappers;
Expand Down Expand Up @@ -28,10 +29,15 @@ public class AgentService(
public async Task<Chat> Process(Chat chat, string agentId, bool translatePrompt = false)
{
var agent = await agentRepository.GetAgentById(agentId);
if (agent == null)
throw new ArgumentException("Agent not found."); //TODO candidate for NotFound domain exception
if (agent.Context == null)
throw new ArgumentException("Agent context not found.");
if (agent == null)
{
throw new AgentNotFoundException(agentId);
}

if (agent.Context == null)
{
throw new AgentContextNotFoundException(agentId);
}

await notificationService.DispatchNotification(
NotificationMessageBuilder.ProcessingStarted(agentId, agent.CurrentBehaviour), "ReceiveAgentUpdate");
Expand Down Expand Up @@ -115,7 +121,9 @@ public async Task<Chat> GetChatByAgent(string agentId)
{
var agent = await agentRepository.GetAgentById(agentId);
if (agent == null)
throw new Exception("Agent not found."); //TODO good candidate for custom exception
{
throw new AgentNotFoundException(agentId);
}

var chat = await chatRepository.GetChatById(agent.ChatId);
return chat!.ToDomain();
Expand All @@ -125,7 +133,9 @@ public async Task<Chat> Restart(string agentId)
{
var agent = await agentRepository.GetAgentById(agentId);
if (agent == null)
throw new Exception("Agent not found."); //TODO good candidate for custom exception
{
throw new AgentNotFoundException(agentId);
}

var chat = (await chatRepository.GetChatById(agent.ChatId))!.ToDomain();
var llmService = llmServiceFactory.CreateService(agent.Backend ?? maInSettings.BackendType);
Expand Down
7 changes: 6 additions & 1 deletion src/MaIN.Services/Services/ChatService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using MaIN.Domain.Configuration;
using MaIN.Domain.Entities;
using MaIN.Domain.Exceptions;
using MaIN.Domain.Models;
using MaIN.Infrastructure.Repositories.Abstract;
using MaIN.Services.Mappers;
Expand Down Expand Up @@ -89,7 +90,11 @@ public async Task Delete(string id)
public async Task<Chat> GetById(string id)
{
var chatDocument = await chatProvider.GetChatById(id);
if(chatDocument == null) throw new Exception("Chat not found"); //TODO good candidate for custom exception
if (chatDocument == null)
{
throw new ChatNotFoundException(id);
}

return chatDocument.ToDomain();
}

Expand Down
7 changes: 5 additions & 2 deletions src/MaIN.Services/Services/DataSourceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Text;
using System.Text.Json;
using MaIN.Domain.Entities.Agents.AgentSource;
using MaIN.Domain.Exceptions;
using MaIN.Services.Services.Abstract;
using MaIN.Services.Utils;
using MongoDB.Bson;
Expand Down Expand Up @@ -76,8 +77,10 @@ public async Task<string> FetchApiData(object? details, string? filter,
var result = await httpClient.SendAsync(request);
if (!result.IsSuccessStatusCode)
{
throw new Exception(
$"API request failed with status code: {result.StatusCode}"); //TODO candidate for domain exception
throw new ApiRequestFailedException(
result.StatusCode,
apiDetails?.Url + apiDetails?.Query,
apiDetails?.Method ?? string.Empty);
}

var data = await result.Content.ReadAsStringAsync();
Expand Down
Loading