Skip to content

Implement AI chat feature and OpenAI configuration settings#2

Open
Lightczx wants to merge 7 commits into
mainfrom
agent-chat
Open

Implement AI chat feature and OpenAI configuration settings#2
Lightczx wants to merge 7 commits into
mainfrom
agent-chat

Conversation

@Lightczx
Copy link
Copy Markdown
Member

@Lightczx Lightczx commented May 8, 2026

No description provided.

添加AI Chat页面、ChatViewModel、IChatService/OpenAIChatService实现,
并在设置页新增OpenAI API Key、Base URL、默认模型配置项。
Copilot AI review requested due to automatic review settings May 8, 2026 12:01
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an in-app AI chat experience backed by an OpenAI-compatible chat streaming client, along with new Settings fields to configure the API key, base URL, and default model.

Changes:

  • Added a new Chat page + view model to send messages and stream assistant responses.
  • Added OpenAI configuration fields (API key, base URL, default model) to app settings and Settings UI.
  • Introduced a basic Markdown-to-WinUI rendering helper for assistant messages.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
src/Snap.Nicole/ViewModels/SettingsViewModel.cs Exposes OpenAI-related settings properties and raises property-changed notifications.
src/Snap.Nicole/ViewModels/ChatViewModel.cs Implements chat state, send/stop/clear commands, and streams responses into a message list.
src/Snap.Nicole/UI/Xaml/Windows/MainWindow.xaml Adds navigation entry for the new Chat page.
src/Snap.Nicole/UI/Xaml/Pages/SettingsPage.xaml.cs Wires PasswordBox changes to the settings ViewModel for the API key.
src/Snap.Nicole/UI/Xaml/Pages/SettingsPage.xaml Adds UI fields for API key, base URL, and default model.
src/Snap.Nicole/UI/Xaml/Pages/ChatPage.xaml.cs Builds chat bubbles in code-behind based on ViewModel message collection changes.
src/Snap.Nicole/UI/Xaml/Pages/ChatPage.xaml Adds the Chat page layout (messages + input + send/clear buttons).
src/Snap.Nicole/UI/Xaml/Helpers/MarkdownHelper.cs Adds a Markdown-ish renderer for assistant responses (headers, lists, tables, code blocks, links).
src/Snap.Nicole/Services/Settings/AppSettings.cs Adds persisted OpenAI configuration properties to settings model.
src/Snap.Nicole/Services/AI/OpenAIChatService.cs Adds OpenAI streaming chat implementation using configured settings.
src/Snap.Nicole/Services/AI/Models/ToolDefinition.cs Adds tool definition model (currently unused by service).
src/Snap.Nicole/Services/AI/Models/ToolCall.cs Adds tool call model (currently unused by service).
src/Snap.Nicole/Services/AI/Models/ChatRole.cs Adds chat role enum used across chat stack.
src/Snap.Nicole/Services/AI/Models/ChatMessage.cs Adds chat message model used by UI + service.
src/Snap.Nicole/Services/AI/Models/ChatCompletionOptions.cs Adds chat request options model (class/file naming mismatch noted).
src/Snap.Nicole/Services/AI/IChatService.cs Adds chat service abstraction used by ChatViewModel.
src/Snap.Nicole/Resources/SR.resx Adds new localized strings (zh) for Chat + OpenAI settings labels.
src/Snap.Nicole/Resources/SR.en.resx Adds new localized strings (en) for Chat + OpenAI settings labels.
src/Snap.Nicole/Program.cs Registers ChatViewModel and OpenAIChatService in DI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +6 to +8

public string? OpenAIApiKey { get; set; }


<TextBlock Text="{snuxm:StringResource Name=UIXamlPagesSettingsPageLabelOpenAIBaseUrl}" />
<TextBox
Text="{Binding OpenAIBaseUrl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

<TextBlock Text="{snuxm:StringResource Name=UIXamlPagesSettingsPageLabelDefaultModel}" />
<TextBox
Text="{Binding DefaultModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Comment on lines 11 to +14
InitializeComponent();
DataContext = App.Host.Services.GetRequiredService<SettingsViewModel>();
OpenAIApiKeyBox.Password = ViewModel.OpenAIApiKey;
}
Comment on lines +36 to +49
RebuildMessages();
}

private void RebuildMessages()
{
MessagesPanel.Children.Clear();

foreach (ChatMessage message in ViewModel.Messages)
{
FrameworkElement bubble = MarkdownHelper.CreateMessageBubble(message.Role, message.Content, message.ModelId);
MessagesPanel.Children.Add(bubble);
}

// Scroll to bottom
Comment on lines +74 to +81
string[] lines = markdown.Split('\n');
bool inCodeBlock = false;
string codeBuffer = "";
string codeLanguage = "";

for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
Comment on lines +292 to +296
List<string> cells = ParseTableCells(tableLines[r]);
// Pad if fewer cells than columns
while (cells.Count < columnCount) cells.Add("");
AddTableRow(tableGrid, cells, r - 1, isHeader: false);
}
}
catch
{
return new SolidColorBrush(Colors.LightGray);
Comment on lines +17 to +21
public static FrameworkElement CreateMessageBubble(ChatRole role, string content, string? modelId = null)
{
StackPanel panel = new() { Spacing = 4, HorizontalAlignment = HorizontalAlignment.Stretch };

string headerText = role == ChatRole.User ? "You" : (modelId ?? "AI");

namespace Snap.Nicole.Services.AI.Models;

internal sealed class ChatRequestOptions
Lightczx added 3 commits May 8, 2026 22:25
- 新增 `ExtendedAgentResponseUpdate` 和 `ChatRoleKind` 模型替代旧版
- 实现支持分段内容(文本/推理/工具调用/工具结果)的聊天界面渲染
- 重构 `OpenAIChatService` 使用新的 AI 代理接口
- 更新温度参数默认值为 0.3 并新增 TopP 参数
- 移除不再使用的旧模型类
- 修复文件末尾缺少换行符的问题
- 将IChatService重构为IAgentService,支持更灵活的AI代理功能
- 添加模型配置管理功能,支持多模型配置和切换
- 引入WellKnownLocations集中管理常用文件路径
- 更新UI以支持模型配置选择和显示
- 修复拼写错误和方法命名问题
- 清理无用代码和优化导入
- 添加 MiSans 字体资源并实现字体替换功能
- 引入 ICopyFrom 和 IIdentifiable 接口
- 重构设置服务使用 IOptionsProvider 统一接口
- 实现可排序的模型配置列表
- 优化设置页面 UI 和交互逻辑
- 添加 XAML 过渡动画效果
- 修复窗口标题栏样式问题
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 51 out of 55 changed files in this pull request and generated 16 comments.

Comment on lines +54 to +60
ModelProfiles.Add(profile);
if (ModelProfiles.Count == 1)
{
options.CurrentValue.SelectedModelProfileId = profile.Id;
}

SelectedProfile = profile;
Comment on lines +37 to +41
<TextBlock Text="{snuxm:StringResource Name=UIXamlPagesSettingsPageLabelProfileName}" />
<TextBox Text="{Binding SelectedProfile.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

<TextBlock Text="{snuxm:StringResource Name=UIXamlPagesSettingsPageLabelProfileEndpoint}" />
<TextBox Text="{Binding SelectedProfile.Endpoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Comment on lines +43 to +44
<TextBlock Text="{snuxm:StringResource Name=UIXamlPagesSettingsPageLabelProfileApiKey}" />
<TextBox Text="{Binding SelectedProfile.ApiKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Comment on lines +103 to +116
if (!updating)
{
setAction(optionsProvider.CurrentValue, Items);
optionsProvider.Update();
}
}

private void OnItemPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (!updating)
{
setAction(optionsProvider.CurrentValue, Items);
optionsProvider.Update();
}
Comment on lines +21 to +27
public ChatViewModel(IServiceProvider serviceProvider)
{
chatService = serviceProvider.GetRequiredService<IAgentService>();
options = serviceProvider.GetRequiredService<IOptionsProvider<AppSettings>>();

options.OnChange(OnSettingsChanged);
}
Comment on lines +6 to +28
static HMODULE moduleHandle = GetModuleHandleW(L"Microsoft.UI.Xaml.dll");
static std::wstring s_resourceString;

static HRESULT Endpoint(LPCVOID /* PALFontAndScriptServices* */ _this, void* /* xstring_ptr* */ pstrDefaultFontNameString)
{
// HRESULT __fastcall xstring_ptr::CloneBuffer(const wchar_t *buffer, xstring_ptr *pstrCloned)
using CloneBufferFunc = HRESULT(*)(const WCHAR*, void*);
CloneBufferFunc cloneBuffer = reinterpret_cast<CloneBufferFunc>(reinterpret_cast<BYTE*>(moduleHandle) + 0x3B260C);
RETURN_IF_FAILED(cloneBuffer(s_resourceString.c_str(), pstrDefaultFontNameString));

return S_OK;
}

HRESULT PatchFontAndScriptServicesGetDefaultFontNameString(LPCWSTR pResource)
{
s_resourceString = pResource;

RETURN_IF_WIN32_ERROR(DetourTransactionBegin());
RETURN_IF_WIN32_ERROR(DetourUpdateThread(GetCurrentThread()));

// __int64 __fastcall PALFontAndScriptServices::GetDefaultFontNameString(PALFontAndScriptServices *this, xstring_ptr *pstrDefaultFontNameString)
LPVOID target = reinterpret_cast<LPVOID>(reinterpret_cast<BYTE*>(moduleHandle) + 0x191C90);
RETURN_IF_WIN32_ERROR(DetourAttach(&target, &Endpoint));
Comment on lines +7 to +8
// Copyright (c) Microsoft Corporation. All rights reserved.
//

internal static class WellKnownLocations
{
public static string AppIcon = Path.Combine(AppContext.BaseDirectory, "Assets", "Logo.ico");
Comment thread src/Snap.Nicole/Core/ICopyFrom.cs Outdated
@@ -0,0 +1,6 @@
namespace Snap.Nicole.Core;

public interface ICopyFrom<T>
Comment on lines +41 to +46
public ChatClientAgent AsAIAgent(IList<AITool>? tools = default)
{
OpenAIClient client = new(new ApiKeyCredential(ApiKey!), new OpenAIClientOptions()
{
Endpoint = Endpoint.ToUri(),
});
Lightczx added 3 commits May 11, 2026 17:29
1. 移除不必要的静态JsonSerializerOptions字段
2. 简化聊天消息存储逻辑,仅处理有响应消息的场景
3. 优化推理内容的补丁设置逻辑,修复RawRepresentation赋值问题
4. 移除冗余的调试输出代码
5. 添加System.Linq命名空间引用
6. 新增SCME0001代码警告抑制项
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.

2 participants