Skip to content

Add AI client#30

Open
simplicbe wants to merge 1 commit intomasterfrom
f_internal_ai_client
Open

Add AI client#30
simplicbe wants to merge 1 commit intomasterfrom
f_internal_ai_client

Conversation

@simplicbe
Copy link
Copy Markdown
Member

@simplicbe simplicbe commented Aug 31, 2025

Summary by CodeRabbit

  • New Features

    • Start AI conversations with optional model and context.
    • Reply to ongoing conversations.
    • View conversation details, messages, and creation time.
    • Access conversation summaries with the latest message.
    • Support for user, context, and AI-generated message types.
    • Configure embeddings (scope and document count) and connector settings.
  • Chores

    • Added a new internal AI client module and integrated it into the solution.
    • Updated dependencies and project configuration to support the new capabilities.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 31, 2025

Walkthrough

Introduces a new AI internal client project with a public InternalConversationClient capable of starting conversations via a POST to an internal AI endpoint. Adds DTOs for conversations, messages, contexts, and request payloads. Updates the solution to include the new project and references the internal client base package.

Changes

Cohort / File(s) Summary of changes
Project setup
src/Simplic.OxS.AI.InternalClient/Simplic.OxS.AI.InternalClient.csproj, src/simplic-oxs-internal-client.sln
Added new .NET 8 project with nullable/implicit usings, version set, dependency on Simplic.OxS.InternalClient; solution updated to include project and build configurations.
Client implementation
src/Simplic.OxS.AI.InternalClient/InternalConversationClient.cs
New public client extending InternalClientBase; DI constructor; Start(StartConversationRequest) performs POST to "ai/InternalConversation/start" and returns ConversationModel; logs and rethrows on error.
Conversation domain models
.../Model/ConversationModel.cs, .../Model/ConversationSummaryModel.cs, .../Model/MessageModel.cs, .../Model/MessageType.cs, .../Model/ConversationContextModel.cs, .../Model/ContextModel.cs
Added DTOs/enums for conversation, summary, message, message type, and context; properties with XML docs; collections initialized.
Request/config models
.../Model/StartConversationRequest.cs, .../Model/ReplyToConversationRequest.cs, .../Model/EmbeddingModel.cs, .../Model/LLMConnectorModel.cs
Added request and configuration DTOs for starting/replying to conversations and embedding/connector settings with defaults.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App as Caller
  participant ICC as InternalConversationClient
  participant ICB as InternalClientBase
  participant AIS as Internal AI Service

  App->>ICC: Start(StartConversationRequest)
  activate ICC
  ICC->>ICB: Post("ai","InternalConversation","start", model)
  activate ICB
  ICB->>AIS: HTTP POST /ai/InternalConversation/start (model)
  AIS-->>ICB: 200 OK (ConversationModel)
  deactivate ICB
  ICB-->>ICC: ConversationModel
  ICC-->>App: ConversationModel
  deactivate ICC

  alt Error (non-2xx)
    AIS-->>ICB: Error (4xx/5xx)
    ICB-->>ICC: Exception
    ICC-->>App: Rethrow after logging
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I thump the keys with whiskered cheer,
A chat begins, the endpoint near.
New models hop in tidy rows,
Context, messages—off it goes!
With POST I bound, then back I glide—
A ConversationModel by my side. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch f_internal_ai_client

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (17)
src/Simplic.OxS.AI.InternalClient/Simplic.OxS.AI.InternalClient.csproj (1)

3-8: Enable analyzers and XML docs for better API quality

Recommend turning on XML doc generation and .NET analyzers for the new library.

Apply:

   <PropertyGroup>
     <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <Version>1.0.25.831</Version>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <EnableNETAnalyzers>true</EnableNETAnalyzers>
+    <AnalysisMode>AllEnabledByDefault</AnalysisMode>
   </PropertyGroup>

Optional: consider CI-driven versioning (e.g., GitVersion/MinVer) and SourceLink in a follow-up.

src/Simplic.OxS.AI.InternalClient/Model/MessageType.cs (1)

6-22: Consider serializing enum as strings to avoid wire-format ambiguity

If the API expects/benefits from string values, add a JSON converter at the enum.

Apply:

+using System.Text.Json.Serialization;
 
 public enum MessageType
 {
-public enum MessageType
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum MessageType
 {

If the API contract relies on numeric values, ignore this.

Do we have server-side expectations documented for enum wire format?

src/Simplic.OxS.AI.InternalClient/Model/ReplyToConversationRequest.cs (1)

13-16: Doc fix: it's a reply, not an initial message

Minor wording tweak to avoid confusion.

-    /// Gets or sets the initial message
+    /// Gets or sets the reply message
src/Simplic.OxS.AI.InternalClient/Model/ConversationContextModel.cs (1)

18-22: Doc typo and clarity

Fix spelling and phrasing.

-    /// Gets or sets a message, that represents a manual context.
-    /// If this is set, togehter with Name and Id, both will be passed to the AI
+    /// Gets or sets a message that represents a manual context.
+    /// If this is set together with Name and Id, all will be passed to the AI.
src/Simplic.OxS.AI.InternalClient/Model/EmbeddingModel.cs (1)

8-21: Align docs/nullability; clarify intent of Count

Docs say null/empty searches everywhere, but DataType is non-nullable. Also tweak phrasing and Count summary.

     /// <summary>
-    /// Gets or sets whether disabling the embedding model
+    /// Gets or sets whether the embedding model is disabled
     /// </summary>
     public bool Disable { get; set; } = false;

     /// <summary>
-    /// Gets or sets the data type (null/empty string will search everywhere)
+    /// Gets or sets the data type filter (null/empty searches everywhere)
     /// </summary>
-    public string DataType { get; set; } = "";
+    public string? DataType { get; set; }

     /// <summary>
-    /// Gets or sets the used documents (count)
+    /// Gets or sets the number of documents to use
     /// </summary>
     public int Count { get; set; } = 5;
src/simplic-oxs-internal-client.sln (1)

44-45: Use SDK-style C# project GUID for consistency

Most SDK-style C# projects use {9A19103F-16F7-4668-BE54-9A1E7A4F7556}. Optional, but keeps the solution consistent.

-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Simplic.OxS.AI.InternalClient", "Simplic.OxS.AI.InternalClient\Simplic.OxS.AI.InternalClient.csproj", "{534E219A-427D-167C-9550-D87FCE45AE63}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Simplic.OxS.AI.InternalClient", "Simplic.OxS.AI.InternalClient\Simplic.OxS.AI.InternalClient.csproj", "{534E219A-427D-167C-9550-D87FCE45AE63}"
src/Simplic.OxS.AI.InternalClient/Model/LLMConnectorModel.cs (1)

8-11: Fix copy/paste in XML doc

Property doc mentions “embedding model” but this is the LLM connector.

     /// <summary>
-    /// Gets or sets whether disabling the embedding model
+    /// Gets or sets whether the LLM connector is disabled
     /// </summary>
     public bool Disable { get; set; } = true;
src/Simplic.OxS.AI.InternalClient/Model/ConversationSummaryModel.cs (1)

16-16: Make LastMessage required (or nullable) to satisfy NRT

With nullable enabled, a non-nullable property without default can be null post-deserialization. Mark as required (preferred) or make it nullable.

-    public MessageModel LastMessage { get; set; }
+    public required MessageModel LastMessage { get; set; }

If the last message can be absent:

-    public MessageModel LastMessage { get; set; }
+    public MessageModel? LastMessage { get; set; }
src/Simplic.OxS.AI.InternalClient/InternalConversationClient.cs (2)

29-31: Fix XML doc param name

Param name is “model”, not “id”.

-        /// <param name="id">The employee id.</param>
+        /// <param name="model">The start conversation request.</param>

30-35: Consider CancellationToken for outbound call

Expose a CancellationToken to allow request aborts/timeouts to propagate, if the base Post supports it.

src/Simplic.OxS.AI.InternalClient/Model/MessageModel.cs (3)

18-22: Prefer DateTimeOffset and a clearer property name (CreatedAt).

Avoid timezone bugs and ambiguity with a property named exactly like the type.

-    /// <summary>
-    /// Gets or sets the datetime the message was created
-    /// </summary>
-    public DateTime DateTime { get; set; }
+    /// <summary>
+    /// Gets or sets the timestamp the message was created (UTC).
+    /// </summary>
+    public DateTimeOffset CreatedAt { get; set; }

26-26: Make Body required for user/LLM messages (if API expects content).

If messages must contain text, enforce it with data annotations.

+using System.ComponentModel.DataAnnotations;
 namespace Simplic.OxS.AI.InternalClient.Model;
@@
-    public string? Body { get; set; }
+    [Required, MinLength(1)]
+    public string? Body { get; set; }

Also applies to: 1-1


14-16: Doc nits: casing and terminology.

  • “llm” → “LLM”
  • “datetime” → “date and time”

Also applies to: 18-21, 33-36

src/Simplic.OxS.AI.InternalClient/Model/ConversationModel.cs (3)

33-36: Use DateTimeOffset and consistent naming (CreatedAt).

Align with MessageModel and prefer UTC-aware timestamps.

-    /// <summary>
-    /// Gets or sets the creation date time
-    /// </summary>
-    public DateTime CreateDateTime { get; set; }
+    /// <summary>
+    /// Gets or sets the creation timestamp (UTC).
+    /// </summary>
+    public DateTimeOffset CreatedAt { get; set; }

28-31: Expose messages as read-only to prevent unintended mutation.

DTOs often benefit from read-only collections.

-    public IList<MessageModel> Messages { get; set; } = new List<MessageModel>();
+    public IReadOnlyList<MessageModel> Messages { get; init; } = new List<MessageModel>();

14-16: Doc nits: “llm” → “LLM”; consider clarifying “Model” as “ModelId”.

Minor clarity/casing improvements.

Also applies to: 18-22

src/Simplic.OxS.AI.InternalClient/Model/StartConversationRequest.cs (1)

30-33: Consider requiring an initial message.

If starting a conversation needs content, enforce it.

-    public string? Message { get; set; }
+    [Required, MinLength(1)]
+    public string? Message { get; set; }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5b99163 and 90eab0e.

📒 Files selected for processing (13)
  • src/Simplic.OxS.AI.InternalClient/InternalConversationClient.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/ContextModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/ConversationContextModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/ConversationModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/ConversationSummaryModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/EmbeddingModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/LLMConnectorModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/MessageModel.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/MessageType.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/ReplyToConversationRequest.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Model/StartConversationRequest.cs (1 hunks)
  • src/Simplic.OxS.AI.InternalClient/Simplic.OxS.AI.InternalClient.csproj (1 hunks)
  • src/simplic-oxs-internal-client.sln (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-04T05:59:33.536Z
Learnt from: MEichhoff
PR: simplic/simplic-oxs-accounting#66
File: src/Simplic.OxS.Accounting/Model/Contract/Parties/ContractParty.cs:0-0
Timestamp: 2025-06-04T05:59:33.536Z
Learning: This project has implicit usings enabled (<ImplicitUsings>enable</ImplicitUsings> in .csproj files), which automatically includes common namespaces like System, so explicit using statements for basic types like DateTime, string, etc. are not needed.

Applied to files:

  • src/Simplic.OxS.AI.InternalClient/Simplic.OxS.AI.InternalClient.csproj
🧬 Code graph analysis (4)
src/Simplic.OxS.AI.InternalClient/Model/ConversationSummaryModel.cs (1)
src/Simplic.OxS.AI.InternalClient/Model/MessageModel.cs (1)
  • MessageModel (6-37)
src/Simplic.OxS.AI.InternalClient/Model/StartConversationRequest.cs (3)
src/Simplic.OxS.AI.InternalClient/Model/ConversationContextModel.cs (1)
  • ConversationContextModel (6-23)
src/Simplic.OxS.AI.InternalClient/Model/EmbeddingModel.cs (1)
  • EmbeddingModel (6-22)
src/Simplic.OxS.AI.InternalClient/Model/LLMConnectorModel.cs (1)
  • LLMConnectorModel (6-12)
src/Simplic.OxS.AI.InternalClient/InternalConversationClient.cs (2)
src/Simplic.OxS.AI.InternalClient/Model/ConversationModel.cs (1)
  • ConversationModel (6-37)
src/Simplic.OxS.AI.InternalClient/Model/StartConversationRequest.cs (1)
  • StartConversationRequest (8-34)
src/Simplic.OxS.AI.InternalClient/Model/ConversationModel.cs (2)
src/Simplic.OxS.AI.InternalClient/Model/ConversationContextModel.cs (1)
  • ConversationContextModel (6-23)
src/Simplic.OxS.AI.InternalClient/Model/MessageModel.cs (1)
  • MessageModel (6-37)
🔇 Additional comments (2)
src/Simplic.OxS.AI.InternalClient/Model/ContextModel.cs (1)

6-12: LGTM

Simple DTO with nullable Name and XML docs looks fine and consistent with implicit usings enabled repo-wide.

src/Simplic.OxS.AI.InternalClient/Model/StartConversationRequest.cs (1)

25-29: Verify Connector default semantics.

LLMConnectorModel.Disable defaults to true (per model snippet). That may unintentionally disable the connector on start. Confirm intent.

Comment on lines +38 to +39
logger.LogError("Could make internal ai call InternalConversation.StartConversation", ex);
throw;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use correct LogError overload and message

Currently logs as a formatted string and drops the exception. Also missing “not”.

-                logger.LogError("Could make internal ai call InternalConversation.StartConversation", ex);
+                logger.LogError(ex, "Could not make internal AI call InternalConversation.StartConversation");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.LogError("Could make internal ai call InternalConversation.StartConversation", ex);
throw;
logger.LogError(ex, "Could not make internal AI call InternalConversation.StartConversation");
throw;
🤖 Prompt for AI Agents
In src/Simplic.OxS.AI.InternalClient/InternalConversationClient.cs around lines
38-39, the LogError call currently uses a formatted-string overload that drops
the exception and the message is missing the word "not"; change the call to the
correct ILogger overload that accepts the exception first and the message second
(e.g., logger.LogError(ex, "Could not make internal ai call
InternalConversation.StartConversation");) so the exception is included in logs
and the message reads correctly.

Comment on lines +13 to +17
/// <summary>
/// Gets or sets the name of the context system
/// </summary>
public string Name { get; set; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Non-nullable string without initialization (CS8618 risk)

With enable, Name should be required or nullable.

Prefer required:

-    public string Name { get; set; }
+    public required string Name { get; set; }

Alternative: make it nullable (string?) if truly optional.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <summary>
/// Gets or sets the name of the context system
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the name of the context system
/// </summary>
public required string Name { get; set; }
🤖 Prompt for AI Agents
In src/Simplic.OxS.AI.InternalClient/Model/ConversationContextModel.cs around
lines 13 to 17, the non-nullable property Name is not initialized which triggers
CS8618 under nullable reference types; either mark the property as required (add
the required modifier to the property) so callers must supply it, or make it
nullable (change type to string?) if it is optional, and update any callers or
constructors accordingly to ensure no uninitialized non-nullable reference
remains.

Comment on lines +11 to +16
public Guid ConversationId { get; set; }

/// <summary>
/// Gets or sets the initial message
/// </summary>
public string? Message { get; set; }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Confirm JSON casing matches the service contract

Properties will serialize as PascalCase unless a camelCase policy is configured globally. Verify alignment with the internal AI endpoint.

Run to check for a global System.Text.Json naming policy:

If camelCase is required and not set globally, annotate with [JsonPropertyName("conversationId")] and [JsonPropertyName("message")].


🏁 Script executed:

#!/bin/bash
rg -nP 'JsonSerializerOptions|JsonNamingPolicy|PropertyNamingPolicy' -C2

Length of output: 74


🏁 Script executed:

#!/bin/bash
# Search for any explicit camelCase naming policy configuration or references
rg -nP 'CamelCase' -C2
rg -nP 'PropertyNamingPolicy' -C2

# Search for instantiation of JsonSerializerOptions or custom options usage
rg -nP 'new JsonSerializerOptions' -C2
rg -nP 'JsonSerializerOptions' -C2

# Search for JSON serialization calls in the client
rg -nP 'JsonSerializer.Serialize' -C2
rg -nP 'PostAsJsonAsync' -C2

# Search for ASP.NET JSON options configuration (if this library configures services)
rg -nP 'AddJsonOptions' -C2
rg -nP 'AddNewtonsoftJson' -C2

Length of output: 258


Add explicit JSON property names for camelCase serialization
No global System.Text.Json naming policy detected; default is PascalCase. To match the internal AI endpoint’s camelCase contract, annotate these in src/Simplic.OxS.AI.InternalClient/Model/ReplyToConversationRequest.cs:

 public Guid ConversationId { get; set; }
+ [JsonPropertyName("conversationId")]
 public Guid ConversationId { get; set; }

 /// <summary>
 /// Gets or sets the initial message
 /// </summary>
+ [JsonPropertyName("message")]
 public string? Message { get; set; }

Add using System.Text.Json.Serialization; if not already present.

🤖 Prompt for AI Agents
In src/Simplic.OxS.AI.InternalClient/Model/ReplyToConversationRequest.cs around
lines 11 to 16, the public properties are currently PascalCase but the internal
AI endpoint expects camelCase; add "using System.Text.Json.Serialization;" to
the file (if missing) and annotate the properties with JsonPropertyName
attributes using camelCase names (e.g., [JsonPropertyName("conversationId")] on
ConversationId and [JsonPropertyName("message")] on Message) so System.Text.Json
serializes them to the expected camelCase contract.

Comment on lines +20 to +24
/// <summary>
/// Gets or sets the embedding model settings
/// </summary>
public EmbeddingModel? Embedding { get; set; } = new EmbeddingModel();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Nullability mismatch: Embedding is nullable but initialized.

Either make it non-nullable or drop the initializer. Recommend non-nullable.

-    public EmbeddingModel? Embedding { get; set; } = new EmbeddingModel();
+    public EmbeddingModel Embedding { get; set; } = new EmbeddingModel();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <summary>
/// Gets or sets the embedding model settings
/// </summary>
public EmbeddingModel? Embedding { get; set; } = new EmbeddingModel();
/// <summary>
/// Gets or sets the embedding model settings
/// </summary>
public EmbeddingModel Embedding { get; set; } = new EmbeddingModel();
🤖 Prompt for AI Agents
In src/Simplic.OxS.AI.InternalClient/Model/StartConversationRequest.cs around
lines 20 to 24, the Embedding property is declared nullable but immediately
initialized; change it to a non-nullable property by removing the trailing '?'
so it reads as public EmbeddingModel Embedding { get; set; } = new
EmbeddingModel(); to reflect that it will always have a value (or alternatively
remove the initializer if you prefer nullable), and ensure the project nullable
context remains consistent.

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