项目文件夹

文件
wehub-resource-sync b957a53def
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:21:23 +08:00

305 行
8.3 KiB
Markdown

---
# These are optional elements. Feel free to remove any of them.
status: accepted
contact: dmytrostruk
date: 2023-12-08
deciders: SergeyMenshykh, markwallace, rbarreto, mabolan, stephentoub, dmytrostruk
consulted:
informed:
---
# Chat Models
## Context and Problem Statement
In latest OpenAI API, `content` property of `chat message` object can accept two types of values `string` or `array` ([Documentation](https://platform.openai.com/docs/api-reference/chat/create)).
We should update current implementation of `ChatMessageContent` class with `string Content` property to support this API.
## Decision Drivers
1. New design should not be coupled to OpenAI API and should work for other AI providers.
2. Naming of classes and properties should be consistent and intuitive.
## Considered Options
Some of the option variations can be combined.
### Option #1: Naming updates and new data type for `chat message content`
Since `chat message content` can be an object now instead of `string`, it requires reserved name for better understanding in domain.
1. `ChatMessageContent` will be renamed to `ChatMessage`. (Same for `StreamingChatMessageContent`).
2. `GetChatMessageContent` methods will be renamed to `GetChatMessage`.
3. New abstract class `ChatMessageContent` that will have property `ChatMessageContentType Type` with values `text`, `image`. (Will be extended with `audio`, `video` in the future).
4. `ChatMessage` will contain collection of `ChatMessageContent` objects `IList<ChatMessageContent> Contents`.
5. There will be concrete implementations of `ChatMessageContent` - `ChatMessageTextContent` and `ChatMessageImageContent`.
New _ChatMessageContentType.cs_
```csharp
public readonly struct ChatMessageContentType : IEquatable<ChatMessageContentType>
{
public static ChatMessageContentType Text { get; } = new("text");
public static ChatMessageContentType Image { get; } = new("image");
public string Label { get; }
// Implementation of `IEquatable`...
}
```
New _ChatMessageContent.cs_
```csharp
public abstract class ChatMessageContent
{
public ChatMessageContentType Type { get; set; }
public ChatMessageContent(ChatMessageContentType type)
{
this.Type = type;
}
}
```
Updated _ChatMessage.cs_:
```csharp
public class ChatMessage : ContentBase
{
public AuthorRole Role { get; set; }
public IList<ChatMessageContent> Contents { get; set; }
```
New _ChatMessageTextContent.cs_
```csharp
public class ChatMessageTextContent : ChatMessageContent
{
public string Text { get; set; }
public ChatMessageTextContent(string text) : base(ChatMessageContentType.Text)
{
this.Text = text;
}
}
```
New _ChatMessageImageContent.cs_
```csharp
public class ChatMessageImageContent : ChatMessageContent
{
public Uri Uri { get; set; }
public ChatMessageImageContent(Uri uri) : base(ChatMessageContentType.Image)
{
this.Uri = uri;
}
}
```
Usage:
```csharp
var chatHistory = new ChatHistory("You are friendly assistant.");
// Construct request
var userContents = new List<ChatMessageContent>
{
new ChatMessageTextContent("What's in this image?"),
new ChatMessageImageContent(new Uri("https://link-to-image.com"))
};
chatHistory.AddUserMessage(userContents);
// Get response
var message = await chatCompletionService.GetChatMessageAsync(chatHistory);
foreach (var content in message.Contents)
{
// Possibility to get content type (text or image).
var contentType = content.Type;
// Cast for specific content type
// Extension methods can be provided for better usability
// (e.g. message GetContent<ChatMessageTextContent>()).
if (content is ChatMessageTextContent textContent)
{
Console.WriteLine(textContent);
}
if (content is ChatMessageImageContent imageContent)
{
Console.WriteLine(imageContent.Uri);
}
}
```
### Option #2: Avoid renaming and new data type for `chat message content`
Same as Option #1, but without naming changes. In order to differentiate actual `chat message` and `chat message content`:
- `Chat Message` will be `ChatMessageContent` (as it is right now).
- `Chat Message Content` will be `ChatMessageContentItem`.
1. New abstract class `ChatMessageContentItem` that will have property `ChatMessageContentItemType Type` with values `text`, `image`. (Will be extended with `audio`, `video` in the future).
2. `ChatMessageContent` will contain collection of `ChatMessageContentItem` objects `IList<ChatMessageContentItem> Items`.
3. There will be concrete implementations of `ChatMessageContentItem` - `ChatMessageTextContentItem` and `ChatMessageImageContentItem`.
New _ChatMessageContentItemType.cs_
```csharp
public readonly struct ChatMessageContentItemType : IEquatable<ChatMessageContentItemType>
{
public static ChatMessageContentItemType Text { get; } = new("text");
public static ChatMessageContentItemType Image { get; } = new("image");
public string Label { get; }
// Implementation of `IEquatable`...
}
```
New _ChatMessageContentItem.cs_
```csharp
public abstract class ChatMessageContentItem
{
public ChatMessageContentItemType Type { get; set; }
public ChatMessageContentItem(ChatMessageContentItemType type)
{
this.Type = type;
}
}
```
Updated _ChatMessageContent.cs_:
```csharp
public class ChatMessageContent : ContentBase
{
public AuthorRole Role { get; set; }
public IList<ChatMessageContentItem> Items { get; set; }
```
New _ChatMessageTextContentItem.cs_
```csharp
public class ChatMessageTextContentItem : ChatMessageContentItem
{
public string Text { get; set; }
public ChatMessageTextContentItem(string text) : base(ChatMessageContentType.Text)
{
this.Text = text;
}
}
```
New _ChatMessageImageContent.cs_
```csharp
public class ChatMessageImageContentItem : ChatMessageContentItem
{
public Uri Uri { get; set; }
public ChatMessageImageContentItem(Uri uri) : base(ChatMessageContentType.Image)
{
this.Uri = uri;
}
}
```
Usage:
```csharp
var chatHistory = new ChatHistory("You are friendly assistant.");
// Construct request
var userContentItems = new List<ChatMessageContentItem>
{
new ChatMessageTextContentItem("What's in this image?"),
new ChatMessageImageContentItem(new Uri("https://link-to-image.com"))
};
chatHistory.AddUserMessage(userContentItems);
// Get response
var message = await chatCompletionService.GetChatMessageContentAsync(chatHistory);
foreach (var contentItem in message.Items)
{
// Possibility to get content type (text or image).
var contentItemType = contentItem.Type;
// Cast for specific content type
// Extension methods can be provided for better usability
// (e.g. message GetContent<ChatMessageTextContentItem>()).
if (contentItem is ChatMessageTextContentItem textContentItem)
{
Console.WriteLine(textContentItem);
}
if (contentItem is ChatMessageImageContentItem imageContentItem)
{
Console.WriteLine(imageContentItem.Uri);
}
}
```
### Option #3: Add new property to `ChatMessageContent` - collection of content items
This option will keep `string Content` property as it is, but will add new property - collection of `ContentBase` items.
Updated _ChatMessageContent.cs_
```csharp
public class ChatMessageContent : ContentBase
{
public AuthorRole Role { get; set; }
public string? Content { get; set; }
public ChatMessageContentItemCollection? Items { get; set; }
}
```
New _ChatMessageContentItemCollection.cs_
```csharp
public class ChatMessageContentItemCollection : IList<ContentBase>, IReadOnlyList<ContentBase>
{
// Implementation of IList<ContentBase>, IReadOnlyList<ContentBase> to catch null values.
}
```
Usage:
```csharp
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var chatHistory = new ChatHistory("You are a friendly assistant.");
chatHistory.AddUserMessage(new ChatMessageContentItemCollection
{
new TextContent("What’s in this image?"),
new ImageContent(new Uri(ImageUri))
});
var reply = await chatCompletionService.GetChatMessageContentAsync(chatHistory);
Console.WriteLine(reply.Content);
```
## Decision Outcome
Option #3 was preferred as it requires small amount of changes to existing hierarchy and provides clean usability for end-user.
Diagram:
![Chat and Text models diagram](diagrams/chat-text-models.png)