Skip to content

Commit 4069ba8

Browse files
committed
Fix noninformative error thrown when modelId is not found
1 parent 57dcdd6 commit 4069ba8

File tree

11 files changed

+79
-30
lines changed

11 files changed

+79
-30
lines changed

src/MaIN.Core/Hub/Contexts/FlowContext.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using MaIN.Domain.Entities;
44
using MaIN.Domain.Entities.Agents;
55
using MaIN.Domain.Entities.Agents.AgentSource;
6+
using MaIN.Domain.Exceptions.Agents;
67
using MaIN.Domain.Exceptions.Flows;
78
using MaIN.Domain.Models.Abstract;
89
using MaIN.Services.Services.Abstract;
@@ -152,7 +153,12 @@ public async Task<ChatResult> ProcessAsync(Chat chat, bool translate = false)
152153
public async Task<ChatResult> ProcessAsync(string message, bool translate = false)
153154
{
154155
var chat = await _agentService.GetChatByAgent(_firstAgent!.Id);
155-
var backend = ModelRegistry.GetById(chat.ModelId).Backend;
156+
if (!ModelRegistry.TryGetById(chat.ModelId, out var model))
157+
{
158+
throw new AgentModelNotAvailableException(_firstAgent.Id, chat.ModelId);
159+
}
160+
161+
var backend = model!.Backend;
156162
chat.Messages.Add(new Message()
157163
{
158164
Content = message,

src/MaIN.Core/Hub/Contexts/Interfaces/ChatContext/IChatBuilderEntryPoint.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public interface IChatBuilderEntryPoint : IChatActions
66
/// Sets the AI model to be used for the current chat session. This determines how the AI will respond to messages
77
/// based on the selected model.
88
/// </summary>
9-
/// <param name="model">The name of the AI model to be used.</param>
9+
/// <param name="model">The ID of the AI model to be used.</param>
1010
/// <returns>The context instance implementing <see cref="IChatMessageBuilder"/> for method chaining.</returns>
1111
IChatMessageBuilder WithModel(string model);
1212

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Net;
2+
3+
namespace MaIN.Domain.Exceptions.Agents;
4+
5+
public class AgentModelNotAvailableException(string agentId, string modelId)
6+
: MaINCustomException($"Model '{modelId}' used by agent '{agentId}' is not registered. If this is a dynamically registered model, it must be re-registered after application restart.")
7+
{
8+
public override string PublicErrorMessage => $"Model '{modelId}' is not available.";
9+
public override HttpStatusCode HttpStatusCode => HttpStatusCode.UnprocessableEntity;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Net;
2+
3+
namespace MaIN.Domain.Exceptions.Chats;
4+
5+
public class ChatModelNotAvailableException(string chatId, string modelId)
6+
: MaINCustomException($"Model '{modelId}' used by chat '{chatId}' is not registered. If this is a dynamically registered model, it must be re-registered after application restart.")
7+
{
8+
public override string PublicErrorMessage => $"Model '{modelId}' is not available.";
9+
public override HttpStatusCode HttpStatusCode => HttpStatusCode.UnprocessableEntity;
10+
}

src/MaIN.Services/Services/ChatService.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ public async Task<ChatResult> Completions(
3939
chat.ImageGen = true;
4040
}
4141

42-
var model = ModelRegistry.GetById(chat.ModelId);
43-
var backend = model.Backend;
42+
if (!ModelRegistry.TryGetById(chat.ModelId, out var model))
43+
{
44+
throw new ChatModelNotAvailableException(chat.Id, chat.ModelId);
45+
}
46+
47+
var backend = model!.Backend;
4448

4549
chat.Messages.Where(x => x.Type == MessageType.NotSet).ToList()
4650
.ForEach(x => x.Type = backend != BackendType.Self ? MessageType.CloudLLM : MessageType.LocalLLM);

src/MaIN.Services/Services/LLMService/LLMService.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,6 @@ private static string FormatToolsForPrompt(ToolsConfiguration toolsConfig)
480480
}
481481
}
482482

483-
484-
485483
return (tokens, isComplete, hasFailed);
486484
}
487485

src/MaIN.Services/Services/Steps/BecomeStepHandler.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using MaIN.Domain.Configuration;
22
using MaIN.Domain.Entities;
3+
using MaIN.Domain.Exceptions.Agents;
34
using MaIN.Domain.Models.Abstract;
45
using MaIN.Services.Services.Abstract;
56
using MaIN.Services.Services.Models;
@@ -34,7 +35,12 @@ public async Task<StepResult> Handle(StepContext context)
3435

3536
await context.NotifyProgress("true", context.Agent.Id, null, context.Agent.CurrentBehaviour, StepName);
3637

37-
var backend = ModelRegistry.GetById(context.Chat.ModelId).Backend;
38+
if (!ModelRegistry.TryGetById(context.Chat.ModelId, out var model))
39+
{
40+
throw new AgentModelNotAvailableException(context.Agent.Id, context.Chat.ModelId);
41+
}
42+
43+
var backend = model!.Backend;
3844
context.Chat.Messages.Add(new()
3945
{
4046
Role = "System",

src/MaIN.Services/Services/Steps/Commands/AnswerCommandHandler.cs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using MaIN.Domain.Configuration;
22
using MaIN.Domain.Entities;
33
using MaIN.Domain.Entities.Agents.Knowledge;
4+
using MaIN.Domain.Exceptions.Agents;
45
using MaIN.Domain.Models;
56
using MaIN.Domain.Models.Abstract;
67
using MaIN.Services.Constants;
@@ -19,8 +20,7 @@ public class AnswerCommandHandler(
1920
ILLMServiceFactory llmServiceFactory,
2021
IMcpService mcpService,
2122
INotificationService notificationService,
22-
IImageGenServiceFactory imageGenServiceFactory,
23-
MaINSettings settings)
23+
IImageGenServiceFactory imageGenServiceFactory)
2424
: ICommandHandler<AnswerCommand, Message?>
2525
{
2626
private static readonly JsonSerializerOptions _jsonOptions = new()
@@ -30,10 +30,13 @@ public class AnswerCommandHandler(
3030

3131
public async Task<Message?> HandleAsync(AnswerCommand command)
3232
{
33+
if (!ModelRegistry.TryGetById(command.Chat.ModelId, out var model))
34+
{
35+
throw new AgentModelNotAvailableException(command.AgentId, command.Chat.ModelId);
36+
}
37+
3338
ChatResult? result;
34-
var backend = ModelRegistry.TryGetById(command.Chat.ModelId, out var resolvedModel)
35-
? resolvedModel!.Backend
36-
: settings.BackendType;
39+
var backend = model!.Backend;
3740
var llmService = llmServiceFactory.CreateService(backend);
3841
var imageGenService = imageGenServiceFactory.CreateService(backend);
3942

@@ -44,15 +47,15 @@ public class AnswerCommandHandler(
4447
new ChatMemoryOptions { Memory = command.Chat.Memory }, new ChatRequestOptions());
4548
return result!.Message;
4649
case KnowledgeUsage.UseKnowledge:
47-
var isKnowledgeNeeded = await ShouldUseKnowledge(command.Knowledge, command.Chat);
50+
var isKnowledgeNeeded = await ShouldUseKnowledge(command.Knowledge, command.Chat, backend);
4851
if (isKnowledgeNeeded)
4952
{
50-
return await ProcessKnowledgeQuery(command.Knowledge, command.Chat, command.AgentId);
53+
return await ProcessKnowledgeQuery(command.Knowledge, command.Chat, command.AgentId, llmService);
5154
}
5255

5356
break;
5457
case KnowledgeUsage.AlwaysUseKnowledge:
55-
return await ProcessKnowledgeQuery(command.Knowledge, command.Chat, command.AgentId);
58+
return await ProcessKnowledgeQuery(command.Knowledge, command.Chat, command.AgentId, llmService);
5659
}
5760

5861
result = command.Chat.ImageGen
@@ -68,7 +71,7 @@ public class AnswerCommandHandler(
6871
return result!.Message;
6972
}
7073

71-
private async Task<bool> ShouldUseKnowledge(Knowledge? knowledge, Chat chat)
74+
private async Task<bool> ShouldUseKnowledge(Knowledge? knowledge, Chat chat, BackendType backend)
7275
{
7376
var originalContent = chat.Messages.Last().Content;
7477

@@ -87,9 +90,6 @@ private async Task<bool> ShouldUseKnowledge(Knowledge? knowledge, Chat chat)
8790
Content of available knowledge has source tags. Prompt: {originalContent}
8891
""";
8992

90-
var backend = ModelRegistry.TryGetById(chat.ModelId, out var resolvedModel)
91-
? resolvedModel!.Backend
92-
: settings.BackendType;
9393
var service = llmServiceFactory.CreateService(backend);
9494

9595
var result = await service.Send(chat, new ChatRequestOptions()
@@ -104,7 +104,7 @@ private async Task<bool> ShouldUseKnowledge(Knowledge? knowledge, Chat chat)
104104
return shouldUseKnowledge;
105105
}
106106

107-
private async Task<Message?> ProcessKnowledgeQuery(Knowledge? knowledge, Chat chat, string agentId)
107+
private async Task<Message?> ProcessKnowledgeQuery(Knowledge? knowledge, Chat chat, string agentId, ILLMService llmService)
108108
{
109109
var originalContent = chat.Messages.Last().Content;
110110
var indexAsKnowledge = knowledge?.Index.Items.ToDictionary(x => x.Name, x => x.Tags);
@@ -116,15 +116,10 @@ private async Task<bool> ShouldUseKnowledge(Knowledge? knowledge, Chat chat)
116116
KNOWLEDGE:
117117
{index}
118118
119-
Find tags that fits user query based on available knowledge (provided to you above as pair of item names with tags).
119+
Find tags that fits user query based on available knowledge (provided to you above as pair of item names with tags).
120120
Always return at least 1 tag in array, and no more than 4. Prompt: {originalContent}
121121
""";
122122

123-
var backend = ModelRegistry.TryGetById(chat.ModelId, out var resolvedModel)
124-
? resolvedModel!.Backend
125-
: settings.BackendType;
126-
var llmService = llmServiceFactory.CreateService(backend);
127-
128123
var searchResult = await llmService.Send(chat, new ChatRequestOptions()
129124
{
130125
SaveConv = false
@@ -134,7 +129,7 @@ Find tags that fits user query based on available knowledge (provided to you abo
134129
.Where(x => x.Tags.Intersect(matchedTags!).Any() || matchedTags!.Contains(x.Name))
135130
.ToList();
136131

137-
//NOTE: perhaps good idea for future to combine knowledge form MCP and from KM
132+
//NOTE: perhaps good idea for future to combine knowledge form MCP and from KM
138133
var memoryOptions = new ChatMemoryOptions();
139134
var mcpConfig = BuildMemoryOptionsFromKnowledgeItems(knowledgeItems, memoryOptions);
140135

src/MaIN.Services/Services/Steps/Commands/RedirectCommandHandler.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using MaIN.Domain.Configuration;
22
using MaIN.Domain.Entities;
3+
using MaIN.Domain.Exceptions.Agents;
34
using MaIN.Domain.Models.Abstract;
45
using MaIN.Services.Services.Abstract;
56
using MaIN.Services.Services.Models.Commands;
@@ -12,7 +13,12 @@ public class RedirectCommandHandler(IAgentService agentService) : ICommandHandle
1213
public async Task<Message?> HandleAsync(RedirectCommand command)
1314
{
1415
var chat = await agentService.GetChatByAgent(command.RelatedAgentId);
15-
var backend = ModelRegistry.GetById(chat.ModelId).Backend;
16+
if (!ModelRegistry.TryGetById(chat.ModelId, out var model))
17+
{
18+
throw new AgentModelNotAvailableException(command.RelatedAgentId, chat.ModelId);
19+
}
20+
21+
var backend = model!.Backend;
1622
chat.Messages.Add(new Message()
1723
{
1824
Role = "User",

src/MaIN.Services/Services/Steps/Commands/StartCommandHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using MaIN.Domain.Configuration;
22
using MaIN.Domain.Entities;
3+
using MaIN.Domain.Exceptions.Agents;
34
using MaIN.Domain.Models.Abstract;
5+
using MaIN.Services.Constants;
46
using MaIN.Services.Services.Models.Commands;
57
using MaIN.Services.Services.Steps.Commands.Abstract;
68

@@ -15,7 +17,13 @@ public class StartCommandHandler : ICommandHandler<StartCommand, Message?>
1517
return Task.FromResult<Message?>(null);
1618
}
1719

18-
var backend = ModelRegistry.GetById(command.Chat.ModelId).Backend;
20+
var agentId = command.Chat.Properties.GetValueOrDefault(ServiceConstants.Properties.AgentIdProperty, command.Chat.Id);
21+
if (!ModelRegistry.TryGetById(command.Chat.ModelId, out var model))
22+
{
23+
throw new AgentModelNotAvailableException(agentId, command.Chat.ModelId);
24+
}
25+
26+
var backend = model!.Backend;
1927
var message = new Message()
2028
{
2129
Content = command.InitialPrompt!,

0 commit comments

Comments
 (0)