手动 develop
parent
4dd23d11fb
commit
17e6c0d8ae
@ -0,0 +1,238 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# 禁用命名规则,因为与 StyleCop 冲突
|
||||||
|
dotnet_diagnostic.SA1300.severity = none
|
||||||
|
# dotnet_diagnostic.SA1301.severity = none
|
||||||
|
# dotnet_diagnostic.SA1302.severity = none
|
||||||
|
# dotnet_diagnostic.SA1303.severity = none
|
||||||
|
# dotnet_diagnostic.SA1304.severity = none
|
||||||
|
# dotnet_diagnostic.SA1305.severity = none
|
||||||
|
# dotnet_diagnostic.SA1306.severity = none
|
||||||
|
# dotnet_diagnostic.SA1307.severity = none
|
||||||
|
# dotnet_diagnostic.SA1308.severity = none
|
||||||
|
# dotnet_diagnostic.SA1309.severity = none
|
||||||
|
# dotnet_diagnostic.SA1310.severity = none
|
||||||
|
# dotnet_diagnostic.SA1311.severity = none
|
||||||
|
# dotnet_diagnostic.SA1312.severity = none
|
||||||
|
# dotnet_diagnostic.SA1313.severity = none
|
||||||
|
# dotnet_diagnostic.SA1314.severity = none
|
||||||
|
|
||||||
|
# SA1649: File name should match first type name
|
||||||
|
dotnet_diagnostic.SA1649.severity = none
|
||||||
|
|
||||||
|
# SA1200: Using directives should be placed correctly
|
||||||
|
dotnet_diagnostic.SA1200.severity = none
|
||||||
|
|
||||||
|
# SA1629: Documentation text should end with a period
|
||||||
|
dotnet_diagnostic.SA1629.severity = none
|
||||||
|
|
||||||
|
# SA1512: Single-line comments should not be followed by blank line
|
||||||
|
dotnet_diagnostic.SA1512.severity = none
|
||||||
|
|
||||||
|
# Element documentation header should be preceded by blank line
|
||||||
|
dotnet_diagnostic.SA1514.severity = none
|
||||||
|
|
||||||
|
# SA1516: Elements should be separated by blank line
|
||||||
|
dotnet_diagnostic.SA1516.severity = none
|
||||||
|
|
||||||
|
# SA1515: Single-line comment should be preceded by blank line
|
||||||
|
dotnet_diagnostic.SA1515.severity = none
|
||||||
|
|
||||||
|
# SA1513: Closing brace should be followed by blank line
|
||||||
|
dotnet_diagnostic.SA1513.severity = none
|
||||||
|
|
||||||
|
# SA1113: Comma should be on the same line as previous parameter
|
||||||
|
dotnet_diagnostic.SA1113.severity = none
|
||||||
|
|
||||||
|
# SA1001: Commas should be spaced correctly
|
||||||
|
dotnet_diagnostic.SA1001.severity = none
|
||||||
|
csharp_using_directive_placement = outside_namespace:silent
|
||||||
|
csharp_style_expression_bodied_methods = false:silent
|
||||||
|
csharp_style_expression_bodied_constructors = false:silent
|
||||||
|
csharp_style_expression_bodied_operators = false:silent
|
||||||
|
csharp_style_expression_bodied_properties = true:silent
|
||||||
|
csharp_style_expression_bodied_indexers = true:silent
|
||||||
|
csharp_style_expression_bodied_accessors = true:silent
|
||||||
|
csharp_style_expression_bodied_lambdas = true:silent
|
||||||
|
csharp_style_expression_bodied_local_functions = false:silent
|
||||||
|
csharp_style_conditional_delegate_call = true:suggestion
|
||||||
|
csharp_style_var_for_built_in_types = false:suggestion
|
||||||
|
csharp_style_var_when_type_is_apparent = false:suggestion
|
||||||
|
csharp_style_var_elsewhere = false:suggestion
|
||||||
|
csharp_prefer_simple_using_statement = true:suggestion
|
||||||
|
csharp_prefer_braces = true:silent
|
||||||
|
csharp_style_namespace_declarations = block_scoped:silent
|
||||||
|
|
||||||
|
# SA1201: Elements should appear in the correct order
|
||||||
|
# dotnet_diagnostic.SA1201.severity = none
|
||||||
|
|
||||||
|
[*.{cs,vb}]
|
||||||
|
end_of_line = crlf
|
||||||
|
dotnet_style_qualification_for_field = false:silent
|
||||||
|
dotnet_style_qualification_for_property = false:silent
|
||||||
|
dotnet_style_qualification_for_method = false:silent
|
||||||
|
dotnet_style_qualification_for_event = false:silent
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||||
|
dotnet_code_quality_unused_parameters = all:suggestion
|
||||||
|
dotnet_style_readonly_field = true:suggestion
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||||
|
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||||
|
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
|
||||||
|
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
|
||||||
|
dotnet_style_coalesce_expression = true:suggestion
|
||||||
|
dotnet_style_null_propagation = true:suggestion
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||||
|
dotnet_style_prefer_auto_properties = true:silent
|
||||||
|
dotnet_style_object_initializer = true:suggestion
|
||||||
|
dotnet_style_collection_initializer = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||||
|
dotnet_style_explicit_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||||
|
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||||
|
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
|
||||||
|
dotnet_style_namespace_match_folder = true:suggestion
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:silent
|
||||||
|
tab_width = 4
|
||||||
|
indent_size = 4
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
[*.cs]
|
||||||
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
# 命名规则
|
||||||
|
|
||||||
|
dotnet_naming_rule.接口_should_be_以_i_开始.severity = warning
|
||||||
|
dotnet_naming_rule.接口_should_be_以_i_开始.symbols = 接口
|
||||||
|
dotnet_naming_rule.接口_should_be_以_i_开始.style = 以_i_开始
|
||||||
|
|
||||||
|
dotnet_naming_rule.私有方法_should_be_camel拼写法.severity = warning
|
||||||
|
dotnet_naming_rule.私有方法_should_be_camel拼写法.symbols = 私有方法
|
||||||
|
dotnet_naming_rule.私有方法_should_be_camel拼写法.style = camel拼写法
|
||||||
|
|
||||||
|
dotnet_naming_rule.方法_should_be_帕斯卡拼写法.severity = warning
|
||||||
|
dotnet_naming_rule.方法_should_be_帕斯卡拼写法.symbols = 方法
|
||||||
|
dotnet_naming_rule.方法_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
# 符号规范
|
||||||
|
|
||||||
|
dotnet_naming_symbols.接口.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.接口.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||||
|
dotnet_naming_symbols.接口.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.私有方法.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.私有方法.applicable_accessibilities = private
|
||||||
|
dotnet_naming_symbols.私有方法.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.方法.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.方法.applicable_accessibilities = public
|
||||||
|
dotnet_naming_symbols.方法.required_modifiers =
|
||||||
|
|
||||||
|
# 命名样式
|
||||||
|
|
||||||
|
dotnet_naming_style.以_i_开始.required_prefix = I
|
||||||
|
dotnet_naming_style.以_i_开始.required_suffix =
|
||||||
|
dotnet_naming_style.以_i_开始.word_separator =
|
||||||
|
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.camel拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.camel拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.camel拼写法.word_separator =
|
||||||
|
dotnet_naming_style.camel拼写法.capitalization = camel_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
|
csharp_style_prefer_method_group_conversion = true:silent
|
||||||
|
csharp_style_prefer_top_level_statements = true:silent
|
||||||
|
csharp_prefer_system_threading_lock = true:suggestion
|
||||||
|
csharp_style_prefer_primary_constructors = true:suggestion
|
||||||
|
csharp_prefer_static_anonymous_function = true:suggestion
|
||||||
|
csharp_prefer_static_local_function = true:suggestion
|
||||||
|
csharp_style_prefer_readonly_struct = true:suggestion
|
||||||
|
csharp_style_prefer_readonly_struct_member = true:suggestion
|
||||||
|
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
|
||||||
|
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
|
||||||
|
csharp_style_prefer_switch_expression = true:suggestion
|
||||||
|
csharp_style_prefer_pattern_matching = true:silent
|
||||||
|
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||||
|
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||||
|
csharp_style_prefer_not_pattern = true:suggestion
|
||||||
|
csharp_style_prefer_extended_property_pattern = true:suggestion
|
||||||
|
csharp_style_throw_expression = true:suggestion
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||||
|
csharp_prefer_simple_default_expression = true:suggestion
|
||||||
|
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||||
|
csharp_style_prefer_index_operator = true:suggestion
|
||||||
|
csharp_style_prefer_range_operator = true:suggestion
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||||
|
csharp_style_prefer_implicitly_typed_lambda_expression = true:suggestion
|
||||||
|
csharp_style_prefer_tuple_swap = true:suggestion
|
||||||
|
csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion
|
||||||
|
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||||
|
csharp_style_inlined_variable_declaration = true:suggestion
|
||||||
|
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||||
|
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||||
|
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||||
|
csharp_space_around_binary_operators = before_and_after
|
||||||
|
csharp_indent_labels = one_less_than_current
|
||||||
|
|
||||||
|
# IDE1006: 命名样式
|
||||||
|
dotnet_diagnostic.IDE1006.severity = none
|
||||||
|
|
||||||
|
[*.vb]
|
||||||
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
# 命名规则
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
|
||||||
|
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
# 符号规范
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.类型.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.非字段成员.required_modifiers =
|
||||||
|
|
||||||
|
# 命名样式
|
||||||
|
|
||||||
|
dotnet_naming_style.以_i_开始.required_prefix = I
|
||||||
|
dotnet_naming_style.以_i_开始.required_suffix =
|
||||||
|
dotnet_naming_style.以_i_开始.word_separator =
|
||||||
|
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.2.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AI", "AI.csproj", "{F091FF6C-454D-F725-5E93-41983547B110}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{F091FF6C-454D-F725-5E93-41983547B110}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F091FF6C-454D-F725-5E93-41983547B110}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F091FF6C-454D-F725-5E93-41983547B110}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F091FF6C-454D-F725-5E93-41983547B110}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {FBDA3407-3A62-4B40-88D5-9D08353B1E4E}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AI.AgentIntegration
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||||
|
public class ActionHandlerAttribute : Attribute
|
||||||
|
{
|
||||||
|
public AppActionType ActionType { get; }
|
||||||
|
|
||||||
|
public ActionHandlerAttribute(AppActionType actionType)
|
||||||
|
{
|
||||||
|
ActionType = actionType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AI.AgentIntegration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 定义代理的操作模式
|
||||||
|
/// </summary>
|
||||||
|
public enum AgentMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 观察模式:仅观察用户操作,不主动干预
|
||||||
|
/// </summary>
|
||||||
|
Observe,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 协助模式:在用户请求时提供帮助
|
||||||
|
/// </summary>
|
||||||
|
Assist,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控制模式:主动控制系统行为
|
||||||
|
/// </summary>
|
||||||
|
Control,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
namespace AI.AgentIntegration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表示应用程序操作的结果
|
||||||
|
/// </summary>
|
||||||
|
public class AppActionResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置操作是否成功
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置操作结果消息
|
||||||
|
/// </summary>
|
||||||
|
public string? Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置操作返回的数据
|
||||||
|
/// </summary>
|
||||||
|
public object? Data { get; set; }
|
||||||
|
|
||||||
|
public static AppActionResult Sucess(string message, object? data = null)
|
||||||
|
{
|
||||||
|
return new AppActionResult()
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = message,
|
||||||
|
Data = data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppActionResult Fail(string message)
|
||||||
|
{
|
||||||
|
return new AppActionResult()
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,164 @@
|
|||||||
|
namespace AI.AgentIntegration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 定义应用程序可执行的操作类型
|
||||||
|
/// </summary>
|
||||||
|
public enum AppActionType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取打开的标签页列表
|
||||||
|
/// </summary>
|
||||||
|
GetOpenTabs,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打开文件
|
||||||
|
/// </summary>
|
||||||
|
OpenFile,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭当前文件
|
||||||
|
/// </summary>
|
||||||
|
CloseFile,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭所有文件
|
||||||
|
/// </summary>
|
||||||
|
CloseAllFiles,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存当前文件
|
||||||
|
/// </summary>
|
||||||
|
SaveFile,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存所有文件
|
||||||
|
/// </summary>
|
||||||
|
SaveAll,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重命名文件
|
||||||
|
/// </summary>
|
||||||
|
RenameFile,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重新加载文件
|
||||||
|
/// </summary>
|
||||||
|
ReloadFile,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换标签页
|
||||||
|
/// </summary>
|
||||||
|
SwitchTab,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导航到指定视图
|
||||||
|
/// </summary>
|
||||||
|
Navigate,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 后退导航
|
||||||
|
/// </summary>
|
||||||
|
NavigateBack,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 前进导航
|
||||||
|
/// </summary>
|
||||||
|
NavigateForward,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置系统忙状态
|
||||||
|
/// </summary>
|
||||||
|
SetBusy,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新界面
|
||||||
|
/// </summary>
|
||||||
|
Refresh,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 退出应用程序
|
||||||
|
/// </summary>
|
||||||
|
Exit,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 撤销操作
|
||||||
|
/// </summary>
|
||||||
|
Undo,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重做操作
|
||||||
|
/// </summary>
|
||||||
|
Redo,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加比例尺
|
||||||
|
/// </summary>
|
||||||
|
AddScaleBar,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加边框
|
||||||
|
/// </summary>
|
||||||
|
AddBorder,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加图例
|
||||||
|
/// </summary>
|
||||||
|
AddLegend,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 描述当前状态
|
||||||
|
/// </summary>
|
||||||
|
DescribeState,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载 xyz 文件
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleLoadXyz,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取列信息
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleGetColumns,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 列头匹配
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleMatchColumns,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取网络化参数
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleGetParameters,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置网络化参数
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleSetParameters,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导入数据
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleImport,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据预览
|
||||||
|
/// </summary>
|
||||||
|
GriddingModulePreviewData,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成图
|
||||||
|
/// </summary>
|
||||||
|
GriddingModuleRun,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从文件导入井点数据
|
||||||
|
/// </summary>
|
||||||
|
WellModuleImportWellPoints,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从文件导入井曲线数据
|
||||||
|
/// </summary>
|
||||||
|
WellModuleImportCurves,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AI.AgentIntegration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 定义应用程序控制器的接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IAppController
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当应用程序状态发生变化时触发的事件
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<AppState> StateChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取应用程序的当前状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>当前应用程序状态</returns>
|
||||||
|
AppState GetCurrentState();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行指定的应用程序操作
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">要执行的操作</param>
|
||||||
|
/// <returns>操作执行结果</returns>
|
||||||
|
Task<AppActionResult> ExecuteAsync(AppAction action);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,30 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Data.Core.Plugins;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace AI
|
||||||
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public IServiceProvider? ServiceProvider { get; private set; }
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
// Line below is needed to remove Avalonia data validation.
|
||||||
|
// Without this line you will get duplicate validations from both Avalonia and CT
|
||||||
|
BindingPlugins.DataValidators.RemoveAt(0);
|
||||||
|
var collection = new ServiceCollection();
|
||||||
|
collection.AddCommonServices();
|
||||||
|
var services = collection.BuildServiceProvider();
|
||||||
|
ServiceProvider = services;
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1766626731970" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5056" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M886.7 247.6L713.4 73.4c-6-6-14.2-9.4-22.7-9.4H192c-35.3 0-64 28.7-64 64v768c0 35.3 28.7 64 64 64h640c35.3 0 64-28.7 64-64V270.2c0-8.5-3.3-16.6-9.3-22.6zM832 864c0 17.7-14.3 32-32 32H224c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h384v160c0 35.3 28.7 64 64 64h160v512zM704 288c-17.7 0-32-14.3-32-32V128l160 160H704z" p-id="5057"></path><path d="M671 672H287c-17.7 0-32 14.3-32 32s14.3 32 32 32h384c17.7 0 32-14.3 32-32s-14.3-32-32-32zM287 480c-17.7 0-32 14.3-32 32s14.3 32 32 32h384c17.7 0 32-14.3 32-32s-14.3-32-32-32H287zM287 352h192c17.7 0 32-14.3 32-32s-14.3-32-32-32H287c-17.7 0-32 14.3-32 32s14.3 32 32 32z" p-id="5058"></path></svg>
|
||||||
|
After Width: | Height: | Size: 973 B |
@ -0,0 +1,30 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using AI.Models;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
public class AuthorToColorConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is AuthorType authorType)
|
||||||
|
{
|
||||||
|
return authorType switch
|
||||||
|
{
|
||||||
|
AuthorType.User => new SolidColorBrush(Color.Parse("#EAEEF6")),
|
||||||
|
AuthorType.AI => new SolidColorBrush(Color.Parse("transparent")),
|
||||||
|
AuthorType.Tool => new SolidColorBrush(Color.Parse("#FFF9E6")), // 淡黄色背景,用于工具调用记录
|
||||||
|
_ => Brushes.Transparent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Brushes.Transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
public class FileExtensionConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is string fileName)
|
||||||
|
{
|
||||||
|
string extension = Path.GetExtension(fileName);
|
||||||
|
if (!string.IsNullOrEmpty(extension))
|
||||||
|
{
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
using AI.Utils;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
public class FileSizeConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is long fileSize)
|
||||||
|
{
|
||||||
|
return FileUnit.FormatFileSize(fileSize);
|
||||||
|
}
|
||||||
|
return "0 B";
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using AI.Models.Form;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表单字段类型与 ConverterParameter 相同时可见(用于按类型显示不同控件)
|
||||||
|
/// </summary>
|
||||||
|
public class FormFieldTypeVisibilityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is FormFieldType ft && parameter is string key)
|
||||||
|
{
|
||||||
|
if (string.Equals(key, "NotChoice", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ft != FormFieldType.Choice;
|
||||||
|
return ft.ToString() == key;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Avalonia.Markup.Xaml.Templates;
|
||||||
|
using AI.Models;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
public class MessageTemplateSelector : IDataTemplate
|
||||||
|
{
|
||||||
|
public required DataTemplate TextMessageTemplate { get; set; }
|
||||||
|
public required DataTemplate FileMessageTemplate { get; set; }
|
||||||
|
public required DataTemplate WorkflowStatusTemplate { get; set; }
|
||||||
|
public required DataTemplate FormMessageTemplate { get; set; }
|
||||||
|
public required DataTemplate TableMessageTemplate { get; set; }
|
||||||
|
public required DataTemplate ColumnMatchMessageTemplate { get; set; }
|
||||||
|
public required DataTemplate ParameterSetMessageTemplate { get; set; }
|
||||||
|
public required DataTemplate XyzLoadCardTemplate { get; set; }
|
||||||
|
public required DataTemplate GriddingParamCardTemplate { get; set; }
|
||||||
|
|
||||||
|
public Control? Build(object? param)
|
||||||
|
{
|
||||||
|
if (param is ChatMessageModel message)
|
||||||
|
{
|
||||||
|
return message.Type switch
|
||||||
|
{
|
||||||
|
MessageType.File => FileMessageTemplate.Build(param),
|
||||||
|
MessageType.WorkflowStatus => WorkflowStatusTemplate.Build(param),
|
||||||
|
MessageType.Form => FormMessageTemplate.Build(param),
|
||||||
|
MessageType.Table => TableMessageTemplate.Build(param),
|
||||||
|
MessageType.ColumnMatch => ColumnMatchMessageTemplate.Build(param),
|
||||||
|
MessageType.ParameterSet => ParameterSetMessageTemplate.Build(param),
|
||||||
|
MessageType.KnowledgeBase => TextMessageTemplate?.Build(param),
|
||||||
|
MessageType.XyzLoadCard => XyzLoadCardTemplate.Build(param),
|
||||||
|
MessageType.GriddingParamCard => GriddingParamCardTemplate.Build(param),
|
||||||
|
_ => TextMessageTemplate?.Build(param),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return TextMessageTemplate?.Build(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is ChatMessageModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using AI.Models.SpecialMessages;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 参数集字段类型与 ConverterParameter 相同时可见(用于按类型显示不同控件)
|
||||||
|
/// </summary>
|
||||||
|
public class ParameterSetFieldTypeVisibilityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is ParameterSetFieldType ft && parameter is string key)
|
||||||
|
{
|
||||||
|
return ft.ToString() == key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using AI.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 判断工作流步骤状态是否为已完成(用于显示勾)
|
||||||
|
/// </summary>
|
||||||
|
public class StatusToCompletedConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is WorkflowStepStatus status)
|
||||||
|
{
|
||||||
|
return status == WorkflowStepStatus.Completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using AI.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将工作模式转换为按钮背景颜色
|
||||||
|
/// </summary>
|
||||||
|
public class WorkflowModeToColorConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is WorkflowMode currentMode && parameter is string targetMode)
|
||||||
|
{
|
||||||
|
var isActive = (currentMode == WorkflowMode.Ask && targetMode == "Ask") ||
|
||||||
|
(currentMode == WorkflowMode.Workflow && targetMode == "Workflow");
|
||||||
|
|
||||||
|
return isActive
|
||||||
|
? new SolidColorBrush(Color.Parse("#007AFF"))
|
||||||
|
: new SolidColorBrush(Colors.Transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SolidColorBrush(Colors.Transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using AI.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将工作模式转换为按钮文字颜色
|
||||||
|
/// </summary>
|
||||||
|
public class WorkflowModeToTextColorConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is WorkflowMode currentMode && parameter is string targetMode)
|
||||||
|
{
|
||||||
|
var isActive = (currentMode == WorkflowMode.Ask && targetMode == "Ask") ||
|
||||||
|
(currentMode == WorkflowMode.Workflow && targetMode == "Workflow");
|
||||||
|
|
||||||
|
return isActive
|
||||||
|
? new SolidColorBrush(Colors.White)
|
||||||
|
: new SolidColorBrush(Color.Parse("#666666"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SolidColorBrush(Color.Parse("#666666"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using AI.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace AI.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将工作流步骤状态转换为颜色
|
||||||
|
/// </summary>
|
||||||
|
public class WorkflowStatusToColorConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is WorkflowStepStatus status)
|
||||||
|
{
|
||||||
|
return status switch
|
||||||
|
{
|
||||||
|
WorkflowStepStatus.Completed => new SolidColorBrush(Color.Parse("#4CAF50")),
|
||||||
|
WorkflowStepStatus.Running => new SolidColorBrush(Color.Parse("#2196F3")),
|
||||||
|
WorkflowStepStatus.Failed => new SolidColorBrush(Color.Parse("#F44336")),
|
||||||
|
WorkflowStepStatus.Skipped => new SolidColorBrush(Color.Parse("#9E9E9E")),
|
||||||
|
WorkflowStepStatus.Pending => new SolidColorBrush(Color.Parse("#757575")),
|
||||||
|
_ => new SolidColorBrush(Color.Parse("#757575"))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SolidColorBrush(Color.Parse("#757575"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using AI.Models.Form;
|
||||||
|
|
||||||
|
namespace AI.Interface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表单注册表:根据 formId 获取表单定义。
|
||||||
|
/// </summary>
|
||||||
|
public interface IFormRegistry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 根据表单 ID 获取表单定义,未注册则返回 null。
|
||||||
|
/// </summary>
|
||||||
|
FormDefinition? GetForm(string formId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有已注册的表单定义(用于生成 Schema YAML、供 LLM 上下文等)。
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<FormDefinition> GetAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AI.KnowledgeBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 知识库接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IKnowledgeBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 查询知识库中的信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">查询语句</param>
|
||||||
|
/// <returns>返回匹配的知识条目</returns>
|
||||||
|
Task<string> SearchKnowledgeAsync(string query);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
namespace AI.Models.Form
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表单字段控件/值类型(通用 schema 类型)
|
||||||
|
/// </summary>
|
||||||
|
public enum FormFieldType
|
||||||
|
{
|
||||||
|
/// <summary>单行文本,常用属性: maxLength, placeholder, default</summary>
|
||||||
|
Text,
|
||||||
|
MultiLine,
|
||||||
|
/// <summary>数值,常用属性: min, max, step, default</summary>
|
||||||
|
Number,
|
||||||
|
/// <summary>勾选/开关,常用属性: default</summary>
|
||||||
|
Boolean,
|
||||||
|
FilePath,
|
||||||
|
/// <summary>单选下拉,常用属性: options[], default</summary>
|
||||||
|
Choice,
|
||||||
|
/// <summary>多选,常用属性: options[], default[]</summary>
|
||||||
|
MultiSelect,
|
||||||
|
Json,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AI.Models.Form
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 多选项条目,用于绑定 CheckBox 与 FormFieldEntry.SelectedValues 同步
|
||||||
|
/// </summary>
|
||||||
|
public class MultiSelectOptionItem : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private readonly FormFieldEntry _entry;
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public string Option { get; }
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isSelected == value) return;
|
||||||
|
_isSelected = value;
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
if (!_entry.SelectedValues.Contains(Option))
|
||||||
|
_entry.SelectedValues.Add(Option);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_entry.SelectedValues.Remove(Option);
|
||||||
|
}
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiSelectOptionItem(FormFieldEntry entry, string option, bool isSelected = false)
|
||||||
|
{
|
||||||
|
_entry = entry;
|
||||||
|
Option = option;
|
||||||
|
_isSelected = isSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AI.Models.SpecialMessages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表示一条「必需列 -> 预览列」的匹配项,用于绑定显示。
|
||||||
|
/// </summary>
|
||||||
|
public class ColumnMappingItem : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private string _requiredColumn = string.Empty;
|
||||||
|
private string _matchedColumn = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>必需列名称</summary>
|
||||||
|
public string RequiredColumn
|
||||||
|
{
|
||||||
|
get => _requiredColumn;
|
||||||
|
set => SetProperty(ref _requiredColumn, value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>匹配到的预览列名称(未匹配时可为空)</summary>
|
||||||
|
public string MatchedColumn
|
||||||
|
{
|
||||||
|
get => _matchedColumn;
|
||||||
|
set => SetProperty(ref _matchedColumn, value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 列头匹配特殊消息 - 用于在聊天流中展示「必需列」与「预览列」及其匹配关系。
|
||||||
|
/// </summary>
|
||||||
|
public class ColumnMatchMessage : ISpecialMessage, INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private string _title = "列头匹配";
|
||||||
|
|
||||||
|
/// <summary>消息唯一标识符</summary>
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>类型名称</summary>
|
||||||
|
public string TypeName => "ColumnMatch";
|
||||||
|
|
||||||
|
/// <summary>不需要实时更新</summary>
|
||||||
|
public bool IsLive => false;
|
||||||
|
|
||||||
|
/// <summary>标题(如「列头匹配」)</summary>
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _title;
|
||||||
|
set => SetProperty(ref _title, value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>必需列名称列表</summary>
|
||||||
|
public ObservableCollection<string> RequiredColumns { get; } = new ObservableCollection<string>();
|
||||||
|
|
||||||
|
/// <summary>预览列(可用列)名称列表</summary>
|
||||||
|
public ObservableCollection<string> PreviewColumns { get; } = new ObservableCollection<string>();
|
||||||
|
|
||||||
|
/// <summary>匹配关系:必需列 -> 匹配的预览列</summary>
|
||||||
|
public ObservableCollection<ColumnMappingItem> Mappings { get; } = new ObservableCollection<ColumnMappingItem>();
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>设置必需列与预览列,并清空匹配关系</summary>
|
||||||
|
public void SetColumns(IEnumerable<string> requiredColumns, IEnumerable<string> previewColumns)
|
||||||
|
{
|
||||||
|
RequiredColumns.Clear();
|
||||||
|
foreach (var c in requiredColumns ?? Array.Empty<string>())
|
||||||
|
RequiredColumns.Add(c ?? string.Empty);
|
||||||
|
PreviewColumns.Clear();
|
||||||
|
foreach (var c in previewColumns ?? Array.Empty<string>())
|
||||||
|
PreviewColumns.Add(c ?? string.Empty);
|
||||||
|
Mappings.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>设置匹配关系(必需列 -> 预览列),会清空并替换当前 Mappings</summary>
|
||||||
|
public void SetMappings(IEnumerable<KeyValuePair<string, string>> mappings)
|
||||||
|
{
|
||||||
|
Mappings.Clear();
|
||||||
|
if (mappings == null) return;
|
||||||
|
foreach (var kv in mappings)
|
||||||
|
{
|
||||||
|
Mappings.Add(new ColumnMappingItem
|
||||||
|
{
|
||||||
|
RequiredColumn = kv.Key ?? string.Empty,
|
||||||
|
MatchedColumn = kv.Value ?? string.Empty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
namespace AI.Models.SpecialMessages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 特殊消息接口 - 所有特殊消息类型的基接口
|
||||||
|
/// </summary>
|
||||||
|
public interface ISpecialMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 特殊消息的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
string Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 特殊消息类型名称
|
||||||
|
/// </summary>
|
||||||
|
string TypeName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否需要实时更新
|
||||||
|
/// </summary>
|
||||||
|
bool IsLive { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AI.Models.SpecialMessages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表格数据预览行 - 一行单元格
|
||||||
|
/// </summary>
|
||||||
|
public class TableDataRow : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>该行单元格(与列顺序一致)</summary>
|
||||||
|
public ObservableCollection<string> Cells { get; } = new ObservableCollection<string>();
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
protected void OnPropertyChanged(string propertyName) =>
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表格数据展示特殊消息 - 用于导入数据等在聊天流中做数据预览
|
||||||
|
/// </summary>
|
||||||
|
public class TableDataMessage : ISpecialMessage, INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private string _title = "数据预览";
|
||||||
|
private int _totalRowCount;
|
||||||
|
private int _maxPreviewRows = 50;
|
||||||
|
|
||||||
|
/// <summary>消息唯一标识符</summary>
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>类型名称</summary>
|
||||||
|
public string TypeName => "Table";
|
||||||
|
|
||||||
|
/// <summary>不需要实时更新</summary>
|
||||||
|
public bool IsLive => false;
|
||||||
|
|
||||||
|
/// <summary>标题(如“数据预览”)</summary>
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _title;
|
||||||
|
set => SetProperty(ref _title, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>列名(表头)</summary>
|
||||||
|
public ObservableCollection<string> ColumnNames { get; } = new ObservableCollection<string>();
|
||||||
|
|
||||||
|
/// <summary>预览行数据</summary>
|
||||||
|
public ObservableCollection<TableDataRow> Rows { get; } = new ObservableCollection<TableDataRow>();
|
||||||
|
|
||||||
|
/// <summary>数据总行数(用于显示“共 N 行,仅显示前 M 行”)</summary>
|
||||||
|
public int TotalRowCount
|
||||||
|
{
|
||||||
|
get => _totalRowCount;
|
||||||
|
set => SetProperty(ref _totalRowCount, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>最多预览行数</summary>
|
||||||
|
public int MaxPreviewRows
|
||||||
|
{
|
||||||
|
get => _maxPreviewRows;
|
||||||
|
set => SetProperty(ref _maxPreviewRows, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>是否有多余行未显示</summary>
|
||||||
|
public bool HasMoreRows => TotalRowCount > Rows.Count;
|
||||||
|
|
||||||
|
/// <summary>摘要文案(如“共 100 行,仅显示前 50 行”)</summary>
|
||||||
|
public string SummaryText => $"共 {TotalRowCount} 行,仅显示前 {Rows.Count} 行";
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
|
||||||
|
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||||
|
return false;
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
if (propertyName == nameof(TotalRowCount) || propertyName == nameof(MaxPreviewRows))
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(HasMoreRows));
|
||||||
|
OnPropertyChanged(nameof(SummaryText));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>从列名与行数据批量填充(每行为字符串数组,与列顺序一致)</summary>
|
||||||
|
public void SetData(IEnumerable<string> columnNames, IEnumerable<IEnumerable<string>> rows, int? totalRowCount = null)
|
||||||
|
{
|
||||||
|
ColumnNames.Clear();
|
||||||
|
foreach (var c in columnNames)
|
||||||
|
ColumnNames.Add(c ?? string.Empty);
|
||||||
|
|
||||||
|
Rows.Clear();
|
||||||
|
foreach (var row in rows)
|
||||||
|
{
|
||||||
|
var r = new TableDataRow();
|
||||||
|
foreach (var cell in row)
|
||||||
|
r.Cells.Add(cell?.ToString() ?? string.Empty);
|
||||||
|
Rows.Add(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
TotalRowCount = totalRowCount ?? Rows.Count;
|
||||||
|
OnPropertyChanged(nameof(HasMoreRows));
|
||||||
|
OnPropertyChanged(nameof(SummaryText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AI.Models.SpecialMessages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 工作流状态特殊消息 - 在聊天流中显示工作流进度
|
||||||
|
/// </summary>
|
||||||
|
public class WorkflowStatusMessage : ISpecialMessage, INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private string _title = "工作流进度";
|
||||||
|
private ObservableCollection<WorkflowStepModel> _steps = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型名称
|
||||||
|
/// </summary>
|
||||||
|
public string TypeName => "WorkflowStatus";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 需要实时更新
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLive => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 工作流标题
|
||||||
|
/// </summary>
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _title;
|
||||||
|
set => SetProperty(ref _title, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 工作流步骤列表
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<WorkflowStepModel> Steps
|
||||||
|
{
|
||||||
|
get => _steps;
|
||||||
|
set => SetProperty(ref _steps, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
field = value;
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.SemanticKernel.ChatCompletion;
|
||||||
|
|
||||||
|
namespace AI.Models.Store
|
||||||
|
{
|
||||||
|
public static class ConversationEntryMapper
|
||||||
|
{
|
||||||
|
public static List<ConversationEntryDto> ToDtoList(IEnumerable<ConversationEntry>? entries)
|
||||||
|
{
|
||||||
|
if (entries == null)
|
||||||
|
{
|
||||||
|
return new List<ConversationEntryDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries.Select(e =>
|
||||||
|
{
|
||||||
|
if (e is TextConversationEntry t)
|
||||||
|
{
|
||||||
|
return new ConversationEntryDto
|
||||||
|
{
|
||||||
|
Kind = t.Kind,
|
||||||
|
Role = t.Role.ToString(),
|
||||||
|
Content = t.Content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e is SpecialConversationEntry s)
|
||||||
|
{
|
||||||
|
return new ConversationEntryDto
|
||||||
|
{
|
||||||
|
Kind = s.Kind,
|
||||||
|
Type = s.Type,
|
||||||
|
Payload = s.Payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConversationEntryDto { Kind = e?.Kind ?? string.Empty };
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ConversationEntry> ToEntryList(IEnumerable<ConversationEntryDto>? entries)
|
||||||
|
{
|
||||||
|
var list = new List<ConversationEntry>();
|
||||||
|
if (entries == null)
|
||||||
|
{
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var e in entries)
|
||||||
|
{
|
||||||
|
if (e == null) continue;
|
||||||
|
|
||||||
|
if (string.Equals(e.Kind, "Text", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var role = AuthorRole.User;
|
||||||
|
if (!string.IsNullOrEmpty(e.Role) && Enum.TryParse<AuthorRole>(e.Role, true, out var r))
|
||||||
|
{
|
||||||
|
role = r;
|
||||||
|
}
|
||||||
|
list.Add(new TextConversationEntry(role, e.Content ?? string.Empty));
|
||||||
|
}
|
||||||
|
else if (string.Equals(e.Kind, "Special", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
list.Add(new SpecialConversationEntry(e.Type ?? string.Empty, e.Payload ?? string.Empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
namespace AI.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 工作流步骤状态
|
||||||
|
/// </summary>
|
||||||
|
public enum WorkflowStepStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 待执行
|
||||||
|
/// </summary>
|
||||||
|
Pending,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行中
|
||||||
|
/// </summary>
|
||||||
|
Running,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已完成
|
||||||
|
/// </summary>
|
||||||
|
Completed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 失败
|
||||||
|
/// </summary>
|
||||||
|
Failed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已跳过(被回溯清除)
|
||||||
|
/// </summary>
|
||||||
|
Skipped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SemanticKernel;
|
||||||
|
using AI.AgentIntegration;
|
||||||
|
|
||||||
|
namespace AI.Plugin
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 导入相关功能插件(井点 / 曲线等从文件导入)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controller">应用程序控制器实例</param>
|
||||||
|
public class ImportPlugin(IAppController controller)
|
||||||
|
{
|
||||||
|
private readonly IAppController controller = controller;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从指定路径导入井点数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">井点数据文件路径</param>
|
||||||
|
/// <returns>成功或失败消息</returns>
|
||||||
|
[KernelFunction]
|
||||||
|
[Description("从指定路径导入井点数据")]
|
||||||
|
public async Task<string> ImportWellPointsAsync(
|
||||||
|
[Description("井点数据文件路径")] string path)
|
||||||
|
{
|
||||||
|
var result = await controller.ExecuteAsync(AppAction.CreateImportWellPoints(path));
|
||||||
|
return result.Success ? "井点导入成功" : result.Message ?? "井点导入失败";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从指定路径导入井曲线数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">井曲线数据文件路径</param>
|
||||||
|
/// <returns>成功或失败消息</returns>
|
||||||
|
[KernelFunction]
|
||||||
|
[Description("从指定路径导入井曲线数据")]
|
||||||
|
public async Task<string> ImportWellCurvesAsync(
|
||||||
|
[Description("井曲线数据文件路径")] string path)
|
||||||
|
{
|
||||||
|
var result = await controller.ExecuteAsync(AppAction.CreateImportWellCurves(path));
|
||||||
|
return result.Success ? "曲线导入成功" : result.Message ?? "曲线导入失败";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.SemanticKernel;
|
||||||
|
using AI.KnowledgeBase;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AI.Plugin
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 知识库插件:仅负责根据查询返回知识库内容,不依赖会话或 Store。
|
||||||
|
/// 将结果写入当前会话 Store 由 <see cref="Filter.KnowledgeBaseStoreFilter"/> 在调用完成后统一处理。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="knowledgeBase">知识库实例</param>
|
||||||
|
public class KnowledgeBasePlugin(IKnowledgeBase knowledgeBase)
|
||||||
|
{
|
||||||
|
private readonly IKnowledgeBase knowledgeBase = knowledgeBase;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询知识库中的信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">查询语句</param>
|
||||||
|
/// <returns>返回匹配的知识条目</returns>
|
||||||
|
[KernelFunction]
|
||||||
|
[Description("当需要回答有关特定主题的知识时,搜索知识库。")]
|
||||||
|
[return: Description("从知识库中检索到的相关信息,用于回答用户的问题。")]
|
||||||
|
public async Task<string> QueryKnowledgeBaseAsync(string query)
|
||||||
|
{
|
||||||
|
return await knowledgeBase.SearchKnowledgeAsync(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,209 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using AI.Models;
|
||||||
|
|
||||||
|
namespace AI.Service
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 管理多个聊天会话;会话存储以 YAML 文件持久化到 Sessions 目录。
|
||||||
|
/// </summary>
|
||||||
|
public class ChatSessionManager
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, ChatSession> _sessions = new();
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
private ChatSession? _currentSession;
|
||||||
|
private readonly SessionStorage _storage;
|
||||||
|
|
||||||
|
public ChatSessionManager(SessionStorage? sessionStorage = null)
|
||||||
|
{
|
||||||
|
_storage = sessionStorage ?? new SessionStorage();
|
||||||
|
LoadSessionsFromDisk();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 会话列表变化事件
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? SessionsChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前会话变化事件
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<ChatSession?>? CurrentSessionChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有会话(按更新时间倒序)
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<ChatSession> GetAllSessions()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _sessions.Values.OrderByDescending(s => s.UpdatedAt).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID获取会话
|
||||||
|
/// </summary>
|
||||||
|
public ChatSession? GetSession(string sessionId)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _sessions.TryGetValue(sessionId, out var session) ? session : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置当前活跃会话
|
||||||
|
/// </summary>
|
||||||
|
public ChatSession? CurrentSession
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _currentSession;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_currentSession != value)
|
||||||
|
{
|
||||||
|
_currentSession = value;
|
||||||
|
CurrentSessionChanged?.Invoke(this, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建新会话并设置为当前会话;会订阅存储变更并持久化到 YAML 文件。
|
||||||
|
/// </summary>
|
||||||
|
public ChatSession CreateSession(string? title = null)
|
||||||
|
{
|
||||||
|
var session = new ChatSession(title: title);
|
||||||
|
session.Store.StoreChanged += (_, _) => SaveSession(session);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_sessions[session.Id] = session;
|
||||||
|
}
|
||||||
|
SaveSession(session);
|
||||||
|
CurrentSession = session;
|
||||||
|
SessionsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将会话保存到 YAML 文件
|
||||||
|
/// </summary>
|
||||||
|
public void SaveSession(ChatSession session)
|
||||||
|
{
|
||||||
|
if (session == null) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_storage.Save(session);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 持久化失败不抛,避免影响主流程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除会话(同时删除对应 YAML 文件)
|
||||||
|
/// </summary>
|
||||||
|
public bool DeleteSession(string sessionId)
|
||||||
|
{
|
||||||
|
ChatSession? deletedSession = null;
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_sessions.TryGetValue(sessionId, out deletedSession))
|
||||||
|
{
|
||||||
|
_sessions.Remove(sessionId);
|
||||||
|
if (_currentSession?.Id == sessionId)
|
||||||
|
{
|
||||||
|
_currentSession = _sessions.Values.OrderByDescending(s => s.UpdatedAt).FirstOrDefault();
|
||||||
|
CurrentSessionChanged?.Invoke(this, _currentSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deletedSession != null)
|
||||||
|
{
|
||||||
|
try { _storage.Delete(sessionId); } catch { }
|
||||||
|
SessionsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换当前会话
|
||||||
|
/// </summary>
|
||||||
|
public bool SwitchToSession(string sessionId)
|
||||||
|
{
|
||||||
|
var session = GetSession(sessionId);
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
CurrentSession = session;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前活跃会话数量
|
||||||
|
/// </summary>
|
||||||
|
public int SessionCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _sessions.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查会话是否存在
|
||||||
|
/// </summary>
|
||||||
|
public bool SessionExists(string sessionId)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _sessions.ContainsKey(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSessionsFromDisk()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var loaded = _storage.LoadAll().ToList();
|
||||||
|
if (loaded.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
foreach (var session in loaded)
|
||||||
|
{
|
||||||
|
session.Store.StoreChanged += (_, _) => SaveSession(session);
|
||||||
|
_sessions[session.Id] = session;
|
||||||
|
}
|
||||||
|
_currentSession = loaded.FirstOrDefault();
|
||||||
|
if (_currentSession != null)
|
||||||
|
{
|
||||||
|
CurrentSessionChanged?.Invoke(this, _currentSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SessionsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 加载失败则保持空列表,后续可新建会话
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using AI.Interface;
|
||||||
|
using AI.Models.Form;
|
||||||
|
|
||||||
|
namespace AI.Service
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认表单注册表,内置「加载散点文件」等表单定义。
|
||||||
|
/// </summary>
|
||||||
|
public class FormRegistry : IFormRegistry
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, FormDefinition> _forms = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public FormRegistry()
|
||||||
|
{
|
||||||
|
RegisterLoadXyzForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterLoadXyzForm()
|
||||||
|
{
|
||||||
|
_forms["gridding-load-xyz"] = new FormDefinition
|
||||||
|
{
|
||||||
|
Id = "gridding-load-xyz",
|
||||||
|
Title = "加载散点文件",
|
||||||
|
SubmitTarget = "GriddingModuleLoadXyz",
|
||||||
|
SubmitLabel = "加载",
|
||||||
|
Fields = new List<FormField>
|
||||||
|
{
|
||||||
|
new FormField
|
||||||
|
{
|
||||||
|
Id = "path",
|
||||||
|
Label = "文件路径",
|
||||||
|
Type = FormFieldType.FilePath,
|
||||||
|
Required = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public FormDefinition? GetForm(string formId)
|
||||||
|
{
|
||||||
|
return _forms.TryGetValue(formId ?? string.Empty, out var def) ? def : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<FormDefinition> GetAll()
|
||||||
|
{
|
||||||
|
return _forms.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AI.Interface;
|
||||||
|
using AI.Models;
|
||||||
|
using AI.Models.Form;
|
||||||
|
using AI.ViewModels;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
|
namespace AI.Service
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表单请求通知实现:收到请求后在 UI 线程向当前会话插入表单消息。
|
||||||
|
/// </summary>
|
||||||
|
public class FormRequestNotifier : IFormRequestNotifier
|
||||||
|
{
|
||||||
|
private readonly MainWindowViewModel _mainViewModel;
|
||||||
|
private readonly IFormRegistry _registry;
|
||||||
|
private readonly Dictionary<string, Action<ChatSession?>> _handlers;
|
||||||
|
|
||||||
|
public const string FormIdLoadXyz = "gridding-load-xyz";
|
||||||
|
public const string FormIdGriddingParams = "gridding-parameters";
|
||||||
|
|
||||||
|
public FormRequestNotifier(MainWindowViewModel mainViewModel, IFormRegistry registry)
|
||||||
|
{
|
||||||
|
_mainViewModel = mainViewModel ?? throw new ArgumentNullException(nameof(mainViewModel));
|
||||||
|
_registry = registry ?? throw new ArgumentNullException(nameof(registry));
|
||||||
|
|
||||||
|
_handlers = new Dictionary<string, Action<ChatSession?>>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
[FormIdLoadXyz] = ShowXyzLoadCard,
|
||||||
|
[FormIdGriddingParams] = ShowGriddingParamCard,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RequestForm(string formId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(formId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetSession = CurrentSessionContext.Current;
|
||||||
|
|
||||||
|
if (_handlers.TryGetValue(formId, out var handler))
|
||||||
|
{
|
||||||
|
handler(targetSession);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var definition = _registry.GetForm(formId);
|
||||||
|
if (definition == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() => _mainViewModel.AddFormMessage(definition, targetSession));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowXyzLoadCard(ChatSession? session)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() => _mainViewModel.AddXyzLoadCardMessage(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowGriddingParamCard(ChatSession? session)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() => _mainViewModel.AddGriddingParamCardMessage(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
using AI.AgentIntegration;
|
||||||
|
using AI.Interface;
|
||||||
|
using AI.Service;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using AI.ViewModels;
|
||||||
|
|
||||||
|
namespace AI
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static void AddCommonServices(this IServiceCollection collection)
|
||||||
|
{
|
||||||
|
// 注册会话管理器
|
||||||
|
collection.AddSingleton<ChatSessionManager>();
|
||||||
|
|
||||||
|
// 注册聊天后端
|
||||||
|
collection.AddSingleton<IChatBackend, KernelService>();
|
||||||
|
|
||||||
|
// 注册主窗口 ViewModel
|
||||||
|
collection.AddSingleton<MainWindowViewModel>();
|
||||||
|
|
||||||
|
// 注册表单注册表与表单通知器
|
||||||
|
collection.AddSingleton<IAppController>(_ =>
|
||||||
|
AppControllerHolder.Instance ?? new NoOpAppController());
|
||||||
|
collection.AddSingleton<IFormRegistry, FormRegistry>();
|
||||||
|
collection.AddSingleton<IFormRequestNotifier, FormRequestNotifier>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using AI.ViewModels;
|
||||||
|
using Control = Avalonia.Controls.Control;
|
||||||
|
|
||||||
|
namespace AI
|
||||||
|
{
|
||||||
|
public class ViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public Control? Build(object? data)
|
||||||
|
{
|
||||||
|
if (data is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
|
||||||
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
var control = (Control)Activator.CreateInstance(type)!;
|
||||||
|
control.DataContext = data;
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is ViewModelBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
using AI.Models;
|
||||||
|
|
||||||
|
namespace AI.ViewModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 会话列表项的 ViewModel
|
||||||
|
/// </summary>
|
||||||
|
public class ChatSessionItemViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly ChatSession _session;
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public ChatSessionItemViewModel(ChatSession session)
|
||||||
|
{
|
||||||
|
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 会话ID
|
||||||
|
/// </summary>
|
||||||
|
public string Id => _session.Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 会话标题
|
||||||
|
/// </summary>
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _session.Title;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_session.Title != value)
|
||||||
|
{
|
||||||
|
_session.Title = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedAt => _session.CreatedAt;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最后更新时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime UpdatedAt => _session.UpdatedAt;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息数量
|
||||||
|
/// </summary>
|
||||||
|
public int MessageCount => _session.MessageCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否被选中
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set => SetProperty(ref _isSelected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取底层会话对象
|
||||||
|
/// </summary>
|
||||||
|
public ChatSession Session => _session;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新显示信息(当会话变化时调用)
|
||||||
|
/// </summary>
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(Title));
|
||||||
|
OnPropertyChanged(nameof(UpdatedAt));
|
||||||
|
OnPropertyChanged(nameof(MessageCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,93 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using AI.ViewModels;
|
||||||
|
|
||||||
|
namespace AI.Views
|
||||||
|
{
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
private bool _isUserAtBottom = true; // 跟踪用户是否在底部
|
||||||
|
private const double ScrollThreshold = 50.0; // 距离底部的阈值(像素)
|
||||||
|
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContextChanged += MainWindow_DataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainWindow_DataContextChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is MainWindowViewModel vm)
|
||||||
|
{
|
||||||
|
vm.RequestScrollToBottom += ViewModel_RequestScrollToBottom;
|
||||||
|
|
||||||
|
// 订阅文件选择请求事件
|
||||||
|
vm.RequestFileSelection += async () =>
|
||||||
|
{
|
||||||
|
var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = "选择文件",
|
||||||
|
AllowMultiple = false,
|
||||||
|
FileTypeFilter = new[] { FilePickerFileTypes.All }
|
||||||
|
});
|
||||||
|
return files;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化滚动视图的滚动事件监听
|
||||||
|
if (ChatMessagesScrollViewer != null)
|
||||||
|
{
|
||||||
|
ChatMessagesScrollViewer.ScrollChanged += ChatMessagesScrollViewer_ScrollChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChatMessagesScrollViewer_ScrollChanged(object? sender, ScrollChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is ScrollViewer scrollViewer)
|
||||||
|
{
|
||||||
|
// 检查用户是否在底部(考虑一个小的阈值,避免浮点数精度问题)
|
||||||
|
var offset = scrollViewer.Offset.Y;
|
||||||
|
var extent = scrollViewer.Extent.Height;
|
||||||
|
var viewport = scrollViewer.Viewport.Height;
|
||||||
|
|
||||||
|
// 处理边界情况:如果内容高度小于视口高度,认为在底部
|
||||||
|
if (extent <= viewport)
|
||||||
|
{
|
||||||
|
_isUserAtBottom = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var distanceFromBottom = extent - (offset + viewport);
|
||||||
|
|
||||||
|
// 如果距离底部小于阈值,认为用户在底部
|
||||||
|
_isUserAtBottom = distanceFromBottom <= ScrollThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewModel_RequestScrollToBottom()
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
// 只在用户已经在底部时才自动滚动
|
||||||
|
// 这样如果用户向上滚动查看历史消息,就不会被强制拉回底部
|
||||||
|
if (_isUserAtBottom)
|
||||||
|
{
|
||||||
|
ChatMessagesScrollViewer.ScrollToEnd();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeleteSessionClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Button button && button.Tag is string sessionId)
|
||||||
|
{
|
||||||
|
if (DataContext is MainWindowViewModel vm)
|
||||||
|
{
|
||||||
|
vm.DeleteSessionCommand.Execute(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AI.Workflow
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 规划结果
|
||||||
|
/// </summary>
|
||||||
|
public class Plan
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 规划唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规划目标
|
||||||
|
/// </summary>
|
||||||
|
public string Goal { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规划步骤列表
|
||||||
|
/// </summary>
|
||||||
|
public List<PlanStep> Steps { get; } = new List<PlanStep>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规划步骤
|
||||||
|
/// </summary>
|
||||||
|
public class PlanStep
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 步骤唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 步骤序号
|
||||||
|
/// </summary>
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 步骤描述
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 步骤状态
|
||||||
|
/// </summary>
|
||||||
|
public PlanStepStatus Status { get; set; } = PlanStepStatus.Pending;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行结果
|
||||||
|
/// </summary>
|
||||||
|
public string? Result { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 错误信息
|
||||||
|
/// </summary>
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始执行时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? StartedAt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 完成时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? CompletedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 规划步骤状态
|
||||||
|
/// </summary>
|
||||||
|
public enum PlanStepStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 待执行
|
||||||
|
/// </summary>
|
||||||
|
Pending,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行中
|
||||||
|
/// </summary>
|
||||||
|
Running,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已完成
|
||||||
|
/// </summary>
|
||||||
|
Completed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行失败
|
||||||
|
/// </summary>
|
||||||
|
Failed
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
namespace AI.Workflow
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 工作流状态
|
||||||
|
/// </summary>
|
||||||
|
public enum WorkflowStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 未开始
|
||||||
|
/// </summary>
|
||||||
|
NotStarted,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 运行中
|
||||||
|
/// </summary>
|
||||||
|
Running,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已完成
|
||||||
|
/// </summary>
|
||||||
|
Completed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行失败
|
||||||
|
/// </summary>
|
||||||
|
Failed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已暂停
|
||||||
|
/// </summary>
|
||||||
|
Paused,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已取消
|
||||||
|
/// </summary>
|
||||||
|
Cancelled
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ModelId": "deepseek-v3.2",
|
||||||
|
"ApiKey": "your-api-key-here",
|
||||||
|
"Endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue