Skip to content

Features/refine template config#1321

Draft
iceljc wants to merge 4 commits intoSciSharp:masterfrom
iceljc:features/refine-template-config
Draft

Features/refine template config#1321
iceljc wants to merge 4 commits intoSciSharp:masterfrom
iceljc:features/refine-template-config

Conversation

@iceljc
Copy link
Copy Markdown
Collaborator

@iceljc iceljc commented Apr 10, 2026

No description provided.

@iceljc iceljc marked this pull request as draft April 10, 2026 21:38
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add template LLM configuration support with inheritance

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add template LLM configuration support with response format settings
• Implement agent template detail retrieval with LLM config inheritance
• Use template-specific LLM config in instruct mode execution
• Refactor template config loading and persistence in file repository
Diagram
flowchart LR
  A["Agent Template"] -->|"inherits from"| B["AgentTemplateConfig"]
  B -->|"contains"| C["LlmConfig"]
  C -->|"extends"| D["LlmConfigBase"]
  E["InstructService"] -->|"retrieves"| F["Template Detail"]
  F -->|"uses LLM config"| G["Chat Completion"]
  H["FileRepository"] -->|"loads/saves"| I["template_configs.json"]
  I -->|"provides"| C
Loading

Grey Divider

File Changes

1. src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs ✨ Enhancement +2/-1

Add GetAgentTemplateDetail method signature

src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs


2. src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs ✨ Enhancement +22/-3

Refactor AgentTemplate with config inheritance structure

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs


3. src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs ✨ Enhancement +8/-1

Add JSON serialization attributes and validation property

src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs


View more (14)
4. src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs ✨ Enhancement +2/-0

Add GetAgentTemplateDetail repository method

src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs


5. src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs Formatting +35/-4

Modernize collection initialization syntax

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs


6. src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs ✨ Enhancement +30/-0

Implement GetAgentTemplateDetail with LLM config fallback

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs


7. src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs ✨ Enhancement +26/-6

Apply template LLM config in instruction execution

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs


8. src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs ✨ Enhancement +15/-3

Use template LLM config when building inner agent

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs


9. src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs ✨ Enhancement +82/-2

Persist and retrieve template configs from JSON file

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs


10. src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs ✨ Enhancement +40/-1

Add template config file handling and loading logic

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs


11. src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs Formatting +1/-1

Modernize collection initialization syntax

src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs


12. src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs ✨ Enhancement +43/-0

Create MongoDB model for template LLM configuration

src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs


13. src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs ✨ Enhancement +9/-3

Add response format and LLM config to MongoDB template model

src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs


14. src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs ✨ Enhancement +18/-0

Implement GetAgentTemplateDetail for MongoDB repository

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs


15. tests/BotSharp.LLM.Tests/Core/TestAgentService.cs 🧪 Tests +5/-0

Add GetAgentTemplateDetail mock implementation

tests/BotSharp.LLM.Tests/Core/TestAgentService.cs


16. src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj ⚙️ Configuration changes +4/-0

Add template_configs.json to project resources

src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj


17. src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json ⚙️ Configuration changes +1/-0

Create empty template configs file for agent

src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (2)   📘 Rule violations (4)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (2)
📘\ ≡ Correctness (1) ☼ Reliability (2) ⛨ Security (1)

Grey Divider


Action required

1. No guard for splitIdx 📘
Description
GetAgentTemplateDetail uses LastIndexOf(".") and then calls Substring without guarding for
-1, which can throw at the storage boundary when files don't match the expected naming format.
This violates the requirement to add explicit null/empty/invalid guards at provider boundaries and
return safe fallbacks.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[R746-750]

+                var fileName = file.Split(Path.DirectorySeparatorChar).Last();
+                var splitIdx = fileName.LastIndexOf(".");
+                var name = fileName.Substring(0, splitIdx);
+                var extension = fileName.Substring(splitIdx + 1);
+                if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
Evidence
PR Compliance ID 2 requires null/empty/invalid-input guards at storage/provider boundaries to avoid
runtime exceptions and return safe fallbacks. The new filename parsing can throw
ArgumentOutOfRangeException when splitIdx is -1 (or otherwise invalid) due to missing
validation.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Filename parsing assumes every file name contains a `.` and uses `Substring` with an unchecked index, which can throw and break reads at the repository boundary.

## Issue Context
This code runs against files in a directory and must tolerate unexpected/invalid file names.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Case-sensitive TemplateName match 📘
Description
Template selection compares identifier-like names with ==, which is case-sensitive and can behave
inconsistently for identifier matching. This violates the requirement to use ordinal,
case-insensitive comparisons for identifiers.
Code

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69]

+                var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName);
Evidence
PR Compliance ID 4 requires StringComparison.OrdinalIgnoreCase (or equivalent) for identifier-like
string comparisons. The new code uses x.Name == options.TemplateName instead of an ordinal,
case-insensitive comparison (e.g., IsEqualTo or Equals(..., OrdinalIgnoreCase)).

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Template lookup uses a case-sensitive equality operator for an identifier-like string (`TemplateName`), risking mismatches.

## Issue Context
Identifier comparisons must be culture-invariant and case-insensitive.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Mongo patch drops config 🐞
Description
MongoRepository.PatchAgentTemplate updates only Content, so template ResponseFormat/LlmConfig
changes sent via /agent/{agentId}/templates are silently lost when Mongo storage is used.
Code

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[R574-576]

    public async Task<bool> PatchAgentTemplate(string agentId, AgentTemplate template)
    {
        if (string.IsNullOrEmpty(agentId) || template == null)
Evidence
The OpenAPI patch endpoint forwards AgentTemplate objects (which now include
ResponseFormat/LlmConfig) into AgentService.PatchAgentTemplate, which calls
IBotSharpRepository.PatchAgentTemplate; the Mongo implementation only assigns Content and persists
Templates, so the new config fields are never written. The Mongo template document model was updated
to include these fields, making the omission concrete data loss for Mongo-backed deployments.

src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs[139-145]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs[58-93]
src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs[6-33]
src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
MongoRepository.PatchAgentTemplate persists only `Content`, dropping new per-template fields (`ResponseFormat`, `LlmConfig`) when templates are patched.

### Issue Context
`/agent/{agentId}/templates` uses `AgentTemplate` as request payload, so clients can submit `responseFormat`/`llmConfig` but Mongo storage will ignore them.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

### Implementation notes
Update the found template element to include:
- `foundTemplate.ResponseFormat = template.ResponseFormat;`
- `foundTemplate.LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig);`

(or replace the element in the list with `AgentTemplateMongoElement.ToMongoElement(template)`), then persist the updated templates array.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. response_format unused 🐞
Description
AgentTemplateConfig adds/persists ResponseFormat, but response-format detection still ignores it, so
setting response_format in template_configs.json has no effect on the JSON-repair path.
Code

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[R23-37]

+public class AgentTemplateConfig
+{
+    public string Name { get; set; }
+
+    /// <summary>
+    /// Response format: json, xml, markdown, yaml, etc.
+    /// </summary>
+    [JsonPropertyName("response_format")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public string? ResponseFormat { get; set; }
+
+    [JsonPropertyName("llm_config")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public AgentTemplateLlmConfig? LlmConfig { get; set; }
+}
Evidence
Templates now load/persist ResponseFormat from template configs, but GetTemplateResponseFormat still
infers Json/Text solely from a hard-coded marker inside template content; InstructService.Execute
relies on GetTemplateResponseFormat to decide whether to run JSON repair, so ResponseFormat
configuration is effectively ignored end-to-end.

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]
src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[386-413]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs[153-162]
src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[317-323]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`AgentTemplateConfig.ResponseFormat` is persisted/loaded but never used to determine response format; JSON repair is triggered only by a template content marker.

### Issue Context
This makes `template_configs.json`'s `response_format` ineffective, even though the model and persistence were added in this PR.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs[153-162]
- src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]

### Implementation notes
Update `GetTemplateResponseFormat` to:
1) Resolve the template object (not just its content).
2) If `template.ResponseFormat` is set (e.g., equals "json" case-insensitively), return `ResponseFormatType.Json`.
3) Otherwise fall back to the existing marker-based detection.

Optionally, validate/normalize allowed ResponseFormat strings to avoid surprises.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Cached AgentTemplate returned mutable 📘
Description
GetAgentTemplateDetail is cached and returns a mutable AgentTemplate instance directly, so
downstream callers can mutate the cached object and leak state across requests/components. This
violates the requirement to defensively copy cached/caller-owned mutable objects before exposing
them to downstream code.
Code

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[R108-136]

+#if !DEBUG
+    [SharpCache(10, perInstanceCache: true)]
+#endif
+    public async Task<AgentTemplate?> GetAgentTemplateDetail(string agentId, string templateName)
+    {
+        var template = await _db.GetAgentTemplateDetail(agentId, templateName);
+        if (template == null)
+        {
+            return template;
+        }
+
+        if (template.LlmConfig == null)
+        {
+            var agent = await _db.GetAgent(agentId);
+            if (!string.IsNullOrEmpty(agent?.LlmConfig?.Provider)
+                && !string.IsNullOrEmpty(agent?.LlmConfig?.Model))
+            {
+                template.LlmConfig = new AgentTemplateLlmConfig
+                {
+                    Provider = agent.LlmConfig.Provider,
+                    Model = agent.LlmConfig.Model,
+                    MaxOutputTokens = agent.LlmConfig.MaxOutputTokens,
+                    ReasoningEffortLevel = agent.LlmConfig.ReasoningEffortLevel
+                };
+            }
+        }
+
+        return template;
+    }
Evidence
PR Compliance ID 1 requires defensive copying of cached mutable objects to prevent cross-request
leakage. The method is decorated with SharpCache and returns the cached template object without
cloning, allowing downstream mutation of a shared instance.

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[108-136]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GetAgentTemplateDetail` is cached and returns a mutable `AgentTemplate` instance directly; callers can mutate it and affect subsequent cache hits.

## Issue Context
The compliance requirement calls out cached mutable objects as a key source of cross-request/component state leakage.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[108-136]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Logs full configFile path 📘
Description
Error logs include the full configFile path, which can expose internal filesystem/configuration
details in logs. This violates the requirement to avoid exposing internal configuration values and
to minimize logged sensitive/internal data.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[R526-529]

+        catch (Exception ex)
+        {
+            _logger.LogError(ex, "Error when loading template configs in {configFile}", configFile);
+            return (configs, configFile);
Evidence
PR Compliance ID 3 requires minimizing sensitive/internal configuration details in logs. The new
error logs explicitly include configFile (a full internal path), which can reveal internal
repository layout and configuration locations.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[526-529]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs[139-143]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Error logs include the full internal `configFile` path, which may expose internal configuration/location details.

## Issue Context
Compliance requires logs to avoid sensitive/internal configuration details and use minimal metadata.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[526-529]
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs[139-143]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +746 to +750
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splitIdx = fileName.LastIndexOf(".");
var name = fileName.Substring(0, splitIdx);
var extension = fileName.Substring(splitIdx + 1);
if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. No guard for splitidx 📘 Rule violation ☼ Reliability

GetAgentTemplateDetail uses LastIndexOf(".") and then calls Substring without guarding for
-1, which can throw at the storage boundary when files don't match the expected naming format.
This violates the requirement to add explicit null/empty/invalid guards at provider boundaries and
return safe fallbacks.
Agent Prompt
## Issue description
Filename parsing assumes every file name contains a `.` and uses `Substring` with an unchecked index, which can throw and break reads at the repository boundary.

## Issue Context
This code runs against files in a directory and must tolerate unexpected/invalid file names.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

{
var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName)?.Content ?? string.Empty;
instruction = BuildInstruction(template, options?.Data ?? []);
var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Case-sensitive templatename match 📘 Rule violation ≡ Correctness

Template selection compares identifier-like names with ==, which is case-sensitive and can behave
inconsistently for identifier matching. This violates the requirement to use ordinal,
case-insensitive comparisons for identifiers.
Agent Prompt
## Issue description
Template lookup uses a case-sensitive equality operator for an identifier-like string (`TemplateName`), risking mismatches.

## Issue Context
Identifier comparisons must be culture-invariant and case-insensitive.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 574 to 576
public async Task<bool> PatchAgentTemplate(string agentId, AgentTemplate template)
{
if (string.IsNullOrEmpty(agentId) || template == null)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Mongo patch drops config 🐞 Bug ≡ Correctness

MongoRepository.PatchAgentTemplate updates only Content, so template ResponseFormat/LlmConfig
changes sent via /agent/{agentId}/templates are silently lost when Mongo storage is used.
Agent Prompt
### Issue description
MongoRepository.PatchAgentTemplate persists only `Content`, dropping new per-template fields (`ResponseFormat`, `LlmConfig`) when templates are patched.

### Issue Context
`/agent/{agentId}/templates` uses `AgentTemplate` as request payload, so clients can submit `responseFormat`/`llmConfig` but Mongo storage will ignore them.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

### Implementation notes
Update the found template element to include:
- `foundTemplate.ResponseFormat = template.ResponseFormat;`
- `foundTemplate.LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig);`

(or replace the element in the list with `AgentTemplateMongoElement.ToMongoElement(template)`), then persist the updated templates array.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant