Files

1 line
208 KiB
JSON
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "alex5527467/deepseekchat", "files": {"DeepSeekChat/Services/API/ToolApiService.cs": {"e0efb5d901d4cccf80032360bcc4107a4bbf4553": {"t": 1785746212.8690596, "n": 27568, "h": "908f9f5e8f15cceb", "c": "// ToolApiService.cs - 专门处理工具调用\r\nusing DeepSeekChat.Models;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Serialization;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Net.Http;\r\nusing System.Text;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\nusing static DeepSeekChat.Services.ToolApiService;\r\n\r\nnamespace DeepSeekChat.Services\r\n{\r\n public class ToolApiService : IToolApiService\r\n {\r\n // 工具服务名称常量定义\r\n public static class ToolServices\r\n {\r\n public const string FileRead = \"FileRead\";\r\n public const string FileCreate = \"FileCreate\";\r\n public const string FileWrite = \"FileWrite\";\r\n public const string FileSystem = \"FileSystem\";\r\n public const string Compile = \"Compile\";\r\n public const string FolderBrowser = \"FolderBrowser\";\r\n public const string CommandLine = \"CommandLine\";\r\n }\r\n\r\n private readonly HttpClient _httpClient;\r\n private readonly string _logFilePath;\r\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\r\n\r\n public ToolApiService(string apiKey, string logFilePath = null)\r\n {\r\n _httpClient = new HttpClient()\r\n {\r\n Timeout = TimeSpan.FromMinutes(10)\r\n };\r\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\r\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\r\n\r\n // 设置日志文件路径\r\n _logFilePath = logFilePath ?? GetDefaultLogFilePath();\r\n\r\n // 确保日志目录存在\r\n EnsureLogDirectory();\r\n\r\n LogInfo($\"ToolApiService 初始化完成,日志文件: {_logFilePath}\");\r\n }\r\n\r\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<string> toolServices)\r\n {\r\n LogInfo($\"开始处理工具调用请求\");\r\n LogInfo($\"传入的工具服务: {string.Join(\", \", toolServices ?? new List<string>())}\");\r\n LogInfo($\"对话历史消息数: {conversationHistory?.Count ?? 0}\");\r\n\r\n // 如果没有指定工具服务,返回空工具列表\r\n if (toolServices == null || toolServices.Count == 0)\r\n {\r\n LogInfo(\"未指定工具服务,使用空工具列表\");\r\n return await SendToolRequestAsync(conversationHistory, new List<object>());\r\n }\r\n\r\n // 收集所有选择的工具\r\n var allTools = new List<object>();\r\n\r\n foreach (var toolService in toolServices.Distinct())\r\n {\r\n var tools = GetToolsByService(toolService);\r\n if (tools != null)\r\n {\r\n allTools.AddRange(tools);\r\n LogInfo($\"添加工具服务 '{toolService}',包含 {tools.Count} 个工具\");\r\n }\r\n else\r\n {\r\n LogWarning($\"未找到工具服务: {toolService}\");\r\n }\r\n }\r\n\r\n LogInfo($\"总计添加 {allTools.Count} 个工具\");\r\n return await SendToolRequestAsync(conversationHistory, allTools);\r\n }\r\n\r\n // 私有方法:使用具体的工具列表发送请求\r\n private async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<object> tools)\r\n {\r\n LogInfo($\"开始发送API请求,工具数量: {tools.Count}\");\r\n\r\n var requestData = new ChatRequest\r\n {\r\n Messages = conversationHistory,\r\n MaxTokens = 5000,\r\n Temperature = 0.3,\r\n Tools = tools,\r\n ToolChoice = tools.Count > 0 ? \"auto\" : \"none\"\r\n };\r\n\r\n var settings = new JsonSerializerSettings\r\n {\r\n ContractResolver = new DefaultContractResolver\r\n {\r\n NamingStrategy = new SnakeCaseNamingStrategy()\r\n },\r\n NullValueHandling = NullValueHandling.Ignore,\r\n Formatting = Formatting.Indented // 为日志美化JSON\r\n };\r\n\r\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\r\n\r\n // 记录发送给API的请求内容\r\n LogRequest(jsonContent, tools.Count > 0);\r\n\r\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\r\n\r\n try\r\n {\r\n LogInfo($\"正在发送请求到 DeepSeek API: {ApiUrl}\");\r\n using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));\r\n\r\n var response = await _httpClient.PostAsync(ApiUrl, content, cts.Token);\r\n\r\n LogInfo($\"收到API响应,状态码: {(int)response.StatusCode} {response.StatusCode}\");\r\n response.EnsureSuccessStatusCode();\r\n\r\n var responseJson = await response.Content.ReadAsStringAsync();\r\n\r\n // 记录API返回的内容\r\n LogResponse(responseJson, (int)response.StatusCode);\r\n\r\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\r\n\r\n var result = new ToolCallResponse();\r\n\r\n if (apiResponse?.Choices?.Count > 0)\r\n {\r\n var message = apiResponse.Choices[0].Message;\r\n\r\n result.Content = message.Content ?? string.Empty;\r\n result.ToolCalls = message.ToolCalls;\r\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\r\n result.Success = true;\r\n\r\n if (result.HasToolCalls)\r\n {\r\n // 提取工具调用信息\r\n result.ToolCalls = message.ToolCalls;\r\n LogInfo($\"API返回了 {message.ToolCalls.Count} 个工具调用\");\r\n for (int i = 0; i < message.ToolCalls.Count; i++)\r\n {\r\n var toolCall = message.ToolCalls[i];\r\n LogInfo($\"工具调用 #{i + 1}: {toolCall.Function?.Name}\");\r\n }\r\n }\r\n else\r\n {\r\n LogInfo($\"API返回了文本回复,长度: {result.Content.Length} 字符\");\r\n }\r\n }\r\n else\r\n {\r\n result.Content = \"没有生成有效的回复。\";\r\n result.Success = false;\r\n LogWarning(\"API返回了空响应或无有效选择\");\r\n }\r\n\r\n return result;\r\n }\r\n catch (HttpRequestException ex)\r\n {\r\n LogError($\"工具调用请求失败: {ex.Message}\");\r\n return new ToolCallResponse\r\n {\r\n Content = $\"工具调用请求失败: {ex.Message}\",\r\n Success = false\r\n };\r\n }\r\n catch (Exception ex)\r\n {\r\n LogError($\"处理API响应时发生错误: {ex.Message}\");\r\n return new ToolCallResponse\r\n {\r\n Content = $\"处理API响应时发生错误: {ex.Message}\",\r\n Success = false\r\n };\r\n }\r\n }\r\n\r\n // 根据服务名称获取对应的工具列表\r\n private List<object> GetToolsByService(string toolService)\r\n {\r\n LogDebug($\"获取工具服务: {toolService}\");\r\n return toolService switch\r\n {\r\n ToolServices.FileRead => GetFileReadTools(),\r\n ToolServices.FileCreate => GetFileCreateTools(),\r\n ToolServices.FileWrite => GetFileWriteTools(),\r\n ToolServices.FolderBrowser => GetFolderBrowserTools(),\r\n ToolServices.CommandLine => GetCommandLineTools(),\r\n _ => null\r\n };\r\n }\r\n\r\n public List<string> GetToolsNameByService(string toolService)\r\n {\r\n LogDebug($\"获取工具名称: {toolService}\");\r\n return toolService switch\r\n {\r\n ToolServices.FileRead => [\"read_file\"],\r\n ToolServices.FileCreate => [\"create_file\"],\r\n ToolServices.FileWrite => [\"write_file\"],\r\n ToolServices.FolderBrowser => [\"get_folder_structure_description\"],\r\n ToolServices.CommandLine => [\"execute_command\"],\r\n _ => [\"\"]\r\n };\r\n }\r\n\r\n #region 日志记录方法\r\n\r\n private string GetDefaultLogFilePath()\r\n {\r\n var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\r\n var logDir = Path.Combine(appDataPath, \"DeepSeekChat\", \"Logs\");\r\n var timestamp = DateTime.Now.ToString(\"yyyyMMdd\");\r\n return Path.Combine(logDir, $\"tool_api_{timestamp}.log\");\r\n }\r\n\r\n private void EnsureLogDirectory()\r\n {\r\n var logDir = Path.GetDirectoryName(_logFilePath);\r\n if (!string.IsNullOrEmpty(logDir) && !Directory.Exists(logDir))\r\n {\r\n Directory.CreateDirectory(logDir);\r\n }\r\n }\r\n\r\n private void LogRequest(string jsonContent, bool hasTools)\r\n {\r\n try\r\n {\r\n var logEntry = new StringBuilder();\r\n logEntry.AppendLine(\"=\".PadRight(80, '='));\r\n logEntry.AppendLine($\"请求时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\");\r\n logEntry.AppendLine($\"目标URL: {ApiUrl}\");\r\n logEntry.AppendLine($\"包含工具: {(hasTools ? \"是\" : \"否\")}\");\r\n logEntry.AppendLine(\"请求内容:\");\r\n logEntry.AppendLine(jsonContent);\r\n logEntry.AppendLine();\r\n\r\n File.AppendAllText(_logFilePath, logEntry.ToString());\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n private void LogResponse(string jsonContent, int statusCode)\r\n {\r\n try\r\n {\r\n var logEntry = new StringBuilder();\r\n logEntry.AppendLine($\"响应时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\");\r\n logEntry.AppendLine($\"状态码: {statusCode}\");\r\n logEntry.AppendLine(\"响应内容:\");\r\n\r\n // 尝试美化JSON响应\r\n try\r\n {\r\n var jsonObject = JsonConvert.DeserializeObject(jsonContent);\r\n var formattedJson = JsonConvert.SerializeObject(jsonObject, Formatting.Indented);\r\n logEntry.AppendLine(formattedJson);\r\n }\r\n catch\r\n {\r\n logEntry.AppendLine(jsonContent);\r\n }\r\n\r\n logEntry.AppendLine(\"=\".PadRight(80, '='));\r\n logEntry.AppendLine();\r\n\r\n File.AppendAllText(_logFilePath, logEntry.ToString());\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n private void LogInfo(string message)\r\n {\r\n WriteLog(\"INFO\", message);\r\n }\r\n\r\n private void LogWarning(string message)\r\n {\r\n WriteLog(\"WARN\", message);\r\n }\r\n\r\n private void LogError(string message)\r\n {\r\n WriteLog(\"ERROR\", message);\r\n }\r\n\r\n private void LogDebug(string message)\r\n {\r\n WriteLog(\"DEBUG\", message);\r\n }\r\n\r\n private void WriteLog(string level, string message)\r\n {\r\n try\r\n {\r\n var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\r\n var logMessage = $\"{timestamp} [{level}] {message}\";\r\n\r\n File.AppendAllText(_logFilePath, logMessage + Environment.NewLine);\r\n\r\n // 同时输出到控制台以便调试\r\n Console.WriteLine(logMessage);\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n #endregion\r\n\r\n #region 工具定义方法\r\n\r\n private List<object> GetFolderInfoTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"get_folder_info\",\r\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n folder_path = new\r\n {\r\n type = \"string\",\r\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\r\n }\r\n },\r\n required = new[] { \"folder_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> DeleteFileTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"delete_file\",\r\n description = \"删除文件\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileCreateTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"create_file\",\r\n description = \"创建新文件\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\r\n },\r\n content = new\r\n {\r\n type = \"string\",\r\n description = \"文件内容,默认为空字符串\"\r\n },\r\n overwrite = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否覆盖已存在的文件,默认为false\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileReadTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"read_file\",\r\n description = \"读取文件内容\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\r\n },\r\n encoding = new\r\n {\r\n type = \"string\",\r\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\r\n },\r\n max_size_bytes = new\r\n {\r\n type = \"integer\",\r\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileWriteTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"write_file\",\r\n description = \"写入文件内容\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\r\n },\r\n content = new\r\n {\r\n type = \"string\",\r\n description = \"要写入的文件内容\"\r\n },\r\n encoding = new\r\n {\r\n type = \"string\",\r\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\r\n },\r\n mode = new\r\n {\r\n type = \"string\",\r\n description = \"写入模式:create=仅创建新文件(文件存在则失败)、overwrite=覆盖文件(文件存在则覆盖)、append=在文件末尾追加内容,默认为create\",\r\n @enum = new[] { \"create\", \"overwrite\", \"append\" }\r\n }\r\n },\r\n required = new[] { \"file_path\", \"content\" ,\"mode\"}\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFolderBrowserTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"get_folder_structure_description\",\r\n description = \"浏览本地文件夹,获取文件列表,可以过滤特定文件类型并排除指定文件夹\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n folder_path = new\r\n {\r\n type = \"string\",\r\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\r\n },\r\n include_subdirectories = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否包含子目录,默认为false\"\r\n },\r\n filter_extensions = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"要过滤的文件扩展名列表,如:['.txt', '.pdf', '.jpg'],只返回这些扩展名的文件\"\r\n },\r\n exclude_paths = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"要排除的文件夹名称或路径列表,如:['node_modules', 'bin', 'obj', '.git']。这些文件夹及其所有内容将被忽略\"\r\n }\r\n },\r\n required = new[] { \"folder_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n private List<object> GetCompileTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"compile_csharp\",\r\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n code = new\r\n {\r\n type = \"string\",\r\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\r\n },\r\n outputPath = new\r\n {\r\n type = \"string\",\r\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\r\n },\r\n execute = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\r\n },\r\n includeReferences = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\r\n },\r\n targetFramework = new\r\n {\r\n type = \"string\",\r\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\r\n }\r\n },\r\n required = new[] { \"code\" }\r\n }\r\n }\r\n },\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"compile_and_execute\",\r\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n code = new\r\n {\r\n type = \"string\",\r\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\r\n },\r\n output_path = new\r\n {\r\n type = \"string\",\r\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\r\n },\r\n arguments = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\r\n }\r\n },\r\n required = new[] { \"code\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetCommandLineTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"execute_command\",\r\n description = \"执行Windows命令行程序或系统命令。注意:show_window参数只在wait_for_exit为false时有效,当wait_for_exit为true时始终不显示窗口\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n command = new\r\n {\r\n type = \"string\",\r\n description = \"要执行的命令,如:ipconfig、dir、ping 127.0.0.1 等\"\r\n },\r\n arguments = new\r\n {\r\n type = \"string\",\r\n description = \"命令行参数,默认为空字符串\"\r\n },\r\n working_directory = new\r\n {\r\n type = \"string\",\r\n description = \"命令执行的工作目录,默认为当前程序运行目录\"\r\n },\r\n wait_for_exit = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否等待命令执行完成并获取输出。如果为true,则必须等待执行完成并捕获输出信息(此时不会显示控制台窗口);如果为false,则立即返回进程ID(此时可以通过show_window控制是否显示窗口)\",\r\n @default = true\r\n },\r\n timeout_seconds = new\r\n {\r\n type = \"number\",\r\n description = \"命令执行超时时间(秒),默认为300秒。仅在wait_for_exit为true时有效\",\r\n @default = 300\r\n },\r\n show_window = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否显示控制台窗口。仅在wait_for_exit为false时有效:为true时显示窗口,为false时后台运行。当wait_for_exit为true时此参数无效,始终不显示窗口\",\r\n @default = false\r\n }\r\n },\r\n required = new[] { \"command\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n #endregion\r\n\r\n public void Dispose()\r\n {\r\n LogInfo(\"ToolApiService 正在释放资源\");\r\n _httpClient?.Dispose();\r\n }\r\n }\r\n\r\n public interface IToolApiService : IDisposable\r\n {\r\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<string> toolServices);\r\n }\r\n}"}, "97d4f59dbc06f3e86f41d7f9dd376c4d58fece26": {"t": 1785760656.5190375, "n": 27568, "h": "908f9f5e8f15cceb", "c": "// ToolApiService.cs - 专门处理工具调用\r\nusing DeepSeekChat.Models;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Serialization;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Net.Http;\r\nusing System.Text;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\nusing static DeepSeekChat.Services.ToolApiService;\r\n\r\nnamespace DeepSeekChat.Services\r\n{\r\n public class ToolApiService : IToolApiService\r\n {\r\n // 工具服务名称常量定义\r\n public static class ToolServices\r\n {\r\n public const string FileRead = \"FileRead\";\r\n public const string FileCreate = \"FileCreate\";\r\n public const string FileWrite = \"FileWrite\";\r\n public const string FileSystem = \"FileSystem\";\r\n public const string Compile = \"Compile\";\r\n public const string FolderBrowser = \"FolderBrowser\";\r\n public const string CommandLine = \"CommandLine\";\r\n }\r\n\r\n private readonly HttpClient _httpClient;\r\n private readonly string _logFilePath;\r\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\r\n\r\n public ToolApiService(string apiKey, string logFilePath = null)\r\n {\r\n _httpClient = new HttpClient()\r\n {\r\n Timeout = TimeSpan.FromMinutes(10)\r\n };\r\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\r\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\r\n\r\n // 设置日志文件路径\r\n _logFilePath = logFilePath ?? GetDefaultLogFilePath();\r\n\r\n // 确保日志目录存在\r\n EnsureLogDirectory();\r\n\r\n LogInfo($\"ToolApiService 初始化完成,日志文件: {_logFilePath}\");\r\n }\r\n\r\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<string> toolServices)\r\n {\r\n LogInfo($\"开始处理工具调用请求\");\r\n LogInfo($\"传入的工具服务: {string.Join(\", \", toolServices ?? new List<string>())}\");\r\n LogInfo($\"对话历史消息数: {conversationHistory?.Count ?? 0}\");\r\n\r\n // 如果没有指定工具服务,返回空工具列表\r\n if (toolServices == null || toolServices.Count == 0)\r\n {\r\n LogInfo(\"未指定工具服务,使用空工具列表\");\r\n return await SendToolRequestAsync(conversationHistory, new List<object>());\r\n }\r\n\r\n // 收集所有选择的工具\r\n var allTools = new List<object>();\r\n\r\n foreach (var toolService in toolServices.Distinct())\r\n {\r\n var tools = GetToolsByService(toolService);\r\n if (tools != null)\r\n {\r\n allTools.AddRange(tools);\r\n LogInfo($\"添加工具服务 '{toolService}',包含 {tools.Count} 个工具\");\r\n }\r\n else\r\n {\r\n LogWarning($\"未找到工具服务: {toolService}\");\r\n }\r\n }\r\n\r\n LogInfo($\"总计添加 {allTools.Count} 个工具\");\r\n return await SendToolRequestAsync(conversationHistory, allTools);\r\n }\r\n\r\n // 私有方法:使用具体的工具列表发送请求\r\n private async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<object> tools)\r\n {\r\n LogInfo($\"开始发送API请求,工具数量: {tools.Count}\");\r\n\r\n var requestData = new ChatRequest\r\n {\r\n Messages = conversationHistory,\r\n MaxTokens = 5000,\r\n Temperature = 0.3,\r\n Tools = tools,\r\n ToolChoice = tools.Count > 0 ? \"auto\" : \"none\"\r\n };\r\n\r\n var settings = new JsonSerializerSettings\r\n {\r\n ContractResolver = new DefaultContractResolver\r\n {\r\n NamingStrategy = new SnakeCaseNamingStrategy()\r\n },\r\n NullValueHandling = NullValueHandling.Ignore,\r\n Formatting = Formatting.Indented // 为日志美化JSON\r\n };\r\n\r\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\r\n\r\n // 记录发送给API的请求内容\r\n LogRequest(jsonContent, tools.Count > 0);\r\n\r\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\r\n\r\n try\r\n {\r\n LogInfo($\"正在发送请求到 DeepSeek API: {ApiUrl}\");\r\n using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));\r\n\r\n var response = await _httpClient.PostAsync(ApiUrl, content, cts.Token);\r\n\r\n LogInfo($\"收到API响应,状态码: {(int)response.StatusCode} {response.StatusCode}\");\r\n response.EnsureSuccessStatusCode();\r\n\r\n var responseJson = await response.Content.ReadAsStringAsync();\r\n\r\n // 记录API返回的内容\r\n LogResponse(responseJson, (int)response.StatusCode);\r\n\r\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\r\n\r\n var result = new ToolCallResponse();\r\n\r\n if (apiResponse?.Choices?.Count > 0)\r\n {\r\n var message = apiResponse.Choices[0].Message;\r\n\r\n result.Content = message.Content ?? string.Empty;\r\n result.ToolCalls = message.ToolCalls;\r\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\r\n result.Success = true;\r\n\r\n if (result.HasToolCalls)\r\n {\r\n // 提取工具调用信息\r\n result.ToolCalls = message.ToolCalls;\r\n LogInfo($\"API返回了 {message.ToolCalls.Count} 个工具调用\");\r\n for (int i = 0; i < message.ToolCalls.Count; i++)\r\n {\r\n var toolCall = message.ToolCalls[i];\r\n LogInfo($\"工具调用 #{i + 1}: {toolCall.Function?.Name}\");\r\n }\r\n }\r\n else\r\n {\r\n LogInfo($\"API返回了文本回复,长度: {result.Content.Length} 字符\");\r\n }\r\n }\r\n else\r\n {\r\n result.Content = \"没有生成有效的回复。\";\r\n result.Success = false;\r\n LogWarning(\"API返回了空响应或无有效选择\");\r\n }\r\n\r\n return result;\r\n }\r\n catch (HttpRequestException ex)\r\n {\r\n LogError($\"工具调用请求失败: {ex.Message}\");\r\n return new ToolCallResponse\r\n {\r\n Content = $\"工具调用请求失败: {ex.Message}\",\r\n Success = false\r\n };\r\n }\r\n catch (Exception ex)\r\n {\r\n LogError($\"处理API响应时发生错误: {ex.Message}\");\r\n return new ToolCallResponse\r\n {\r\n Content = $\"处理API响应时发生错误: {ex.Message}\",\r\n Success = false\r\n };\r\n }\r\n }\r\n\r\n // 根据服务名称获取对应的工具列表\r\n private List<object> GetToolsByService(string toolService)\r\n {\r\n LogDebug($\"获取工具服务: {toolService}\");\r\n return toolService switch\r\n {\r\n ToolServices.FileRead => GetFileReadTools(),\r\n ToolServices.FileCreate => GetFileCreateTools(),\r\n ToolServices.FileWrite => GetFileWriteTools(),\r\n ToolServices.FolderBrowser => GetFolderBrowserTools(),\r\n ToolServices.CommandLine => GetCommandLineTools(),\r\n _ => null\r\n };\r\n }\r\n\r\n public List<string> GetToolsNameByService(string toolService)\r\n {\r\n LogDebug($\"获取工具名称: {toolService}\");\r\n return toolService switch\r\n {\r\n ToolServices.FileRead => [\"read_file\"],\r\n ToolServices.FileCreate => [\"create_file\"],\r\n ToolServices.FileWrite => [\"write_file\"],\r\n ToolServices.FolderBrowser => [\"get_folder_structure_description\"],\r\n ToolServices.CommandLine => [\"execute_command\"],\r\n _ => [\"\"]\r\n };\r\n }\r\n\r\n #region 日志记录方法\r\n\r\n private string GetDefaultLogFilePath()\r\n {\r\n var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\r\n var logDir = Path.Combine(appDataPath, \"DeepSeekChat\", \"Logs\");\r\n var timestamp = DateTime.Now.ToString(\"yyyyMMdd\");\r\n return Path.Combine(logDir, $\"tool_api_{timestamp}.log\");\r\n }\r\n\r\n private void EnsureLogDirectory()\r\n {\r\n var logDir = Path.GetDirectoryName(_logFilePath);\r\n if (!string.IsNullOrEmpty(logDir) && !Directory.Exists(logDir))\r\n {\r\n Directory.CreateDirectory(logDir);\r\n }\r\n }\r\n\r\n private void LogRequest(string jsonContent, bool hasTools)\r\n {\r\n try\r\n {\r\n var logEntry = new StringBuilder();\r\n logEntry.AppendLine(\"=\".PadRight(80, '='));\r\n logEntry.AppendLine($\"请求时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\");\r\n logEntry.AppendLine($\"目标URL: {ApiUrl}\");\r\n logEntry.AppendLine($\"包含工具: {(hasTools ? \"是\" : \"否\")}\");\r\n logEntry.AppendLine(\"请求内容:\");\r\n logEntry.AppendLine(jsonContent);\r\n logEntry.AppendLine();\r\n\r\n File.AppendAllText(_logFilePath, logEntry.ToString());\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n private void LogResponse(string jsonContent, int statusCode)\r\n {\r\n try\r\n {\r\n var logEntry = new StringBuilder();\r\n logEntry.AppendLine($\"响应时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\");\r\n logEntry.AppendLine($\"状态码: {statusCode}\");\r\n logEntry.AppendLine(\"响应内容:\");\r\n\r\n // 尝试美化JSON响应\r\n try\r\n {\r\n var jsonObject = JsonConvert.DeserializeObject(jsonContent);\r\n var formattedJson = JsonConvert.SerializeObject(jsonObject, Formatting.Indented);\r\n logEntry.AppendLine(formattedJson);\r\n }\r\n catch\r\n {\r\n logEntry.AppendLine(jsonContent);\r\n }\r\n\r\n logEntry.AppendLine(\"=\".PadRight(80, '='));\r\n logEntry.AppendLine();\r\n\r\n File.AppendAllText(_logFilePath, logEntry.ToString());\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n private void LogInfo(string message)\r\n {\r\n WriteLog(\"INFO\", message);\r\n }\r\n\r\n private void LogWarning(string message)\r\n {\r\n WriteLog(\"WARN\", message);\r\n }\r\n\r\n private void LogError(string message)\r\n {\r\n WriteLog(\"ERROR\", message);\r\n }\r\n\r\n private void LogDebug(string message)\r\n {\r\n WriteLog(\"DEBUG\", message);\r\n }\r\n\r\n private void WriteLog(string level, string message)\r\n {\r\n try\r\n {\r\n var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\r\n var logMessage = $\"{timestamp} [{level}] {message}\";\r\n\r\n File.AppendAllText(_logFilePath, logMessage + Environment.NewLine);\r\n\r\n // 同时输出到控制台以便调试\r\n Console.WriteLine(logMessage);\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n #endregion\r\n\r\n #region 工具定义方法\r\n\r\n private List<object> GetFolderInfoTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"get_folder_info\",\r\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n folder_path = new\r\n {\r\n type = \"string\",\r\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\r\n }\r\n },\r\n required = new[] { \"folder_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> DeleteFileTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"delete_file\",\r\n description = \"删除文件\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileCreateTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"create_file\",\r\n description = \"创建新文件\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\r\n },\r\n content = new\r\n {\r\n type = \"string\",\r\n description = \"文件内容,默认为空字符串\"\r\n },\r\n overwrite = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否覆盖已存在的文件,默认为false\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileReadTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"read_file\",\r\n description = \"读取文件内容\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\r\n },\r\n encoding = new\r\n {\r\n type = \"string\",\r\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\r\n },\r\n max_size_bytes = new\r\n {\r\n type = \"integer\",\r\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileWriteTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"write_file\",\r\n description = \"写入文件内容\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\r\n },\r\n content = new\r\n {\r\n type = \"string\",\r\n description = \"要写入的文件内容\"\r\n },\r\n encoding = new\r\n {\r\n type = \"string\",\r\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\r\n },\r\n mode = new\r\n {\r\n type = \"string\",\r\n description = \"写入模式:create=仅创建新文件(文件存在则失败)、overwrite=覆盖文件(文件存在则覆盖)、append=在文件末尾追加内容,默认为create\",\r\n @enum = new[] { \"create\", \"overwrite\", \"append\" }\r\n }\r\n },\r\n required = new[] { \"file_path\", \"content\" ,\"mode\"}\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFolderBrowserTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"get_folder_structure_description\",\r\n description = \"浏览本地文件夹,获取文件列表,可以过滤特定文件类型并排除指定文件夹\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n folder_path = new\r\n {\r\n type = \"string\",\r\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\r\n },\r\n include_subdirectories = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否包含子目录,默认为false\"\r\n },\r\n filter_extensions = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"要过滤的文件扩展名列表,如:['.txt', '.pdf', '.jpg'],只返回这些扩展名的文件\"\r\n },\r\n exclude_paths = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"要排除的文件夹名称或路径列表,如:['node_modules', 'bin', 'obj', '.git']。这些文件夹及其所有内容将被忽略\"\r\n }\r\n },\r\n required = new[] { \"folder_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n private List<object> GetCompileTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"compile_csharp\",\r\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n code = new\r\n {\r\n type = \"string\",\r\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\r\n },\r\n outputPath = new\r\n {\r\n type = \"string\",\r\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\r\n },\r\n execute = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\r\n },\r\n includeReferences = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\r\n },\r\n targetFramework = new\r\n {\r\n type = \"string\",\r\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\r\n }\r\n },\r\n required = new[] { \"code\" }\r\n }\r\n }\r\n },\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"compile_and_execute\",\r\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n code = new\r\n {\r\n type = \"string\",\r\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\r\n },\r\n output_path = new\r\n {\r\n type = \"string\",\r\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\r\n },\r\n arguments = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\r\n }\r\n },\r\n required = new[] { \"code\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetCommandLineTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"execute_command\",\r\n description = \"执行Windows命令行程序或系统命令。注意:show_window参数只在wait_for_exit为false时有效,当wait_for_exit为true时始终不显示窗口\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n command = new\r\n {\r\n type = \"string\",\r\n description = \"要执行的命令,如:ipconfig、dir、ping 127.0.0.1 等\"\r\n },\r\n arguments = new\r\n {\r\n type = \"string\",\r\n description = \"命令行参数,默认为空字符串\"\r\n },\r\n working_directory = new\r\n {\r\n type = \"string\",\r\n description = \"命令执行的工作目录,默认为当前程序运行目录\"\r\n },\r\n wait_for_exit = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否等待命令执行完成并获取输出。如果为true,则必须等待执行完成并捕获输出信息(此时不会显示控制台窗口);如果为false,则立即返回进程ID(此时可以通过show_window控制是否显示窗口)\",\r\n @default = true\r\n },\r\n timeout_seconds = new\r\n {\r\n type = \"number\",\r\n description = \"命令执行超时时间(秒),默认为300秒。仅在wait_for_exit为true时有效\",\r\n @default = 300\r\n },\r\n show_window = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否显示控制台窗口。仅在wait_for_exit为false时有效:为true时显示窗口,为false时后台运行。当wait_for_exit为true时此参数无效,始终不显示窗口\",\r\n @default = false\r\n }\r\n },\r\n required = new[] { \"command\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n #endregion\r\n\r\n public void Dispose()\r\n {\r\n LogInfo(\"ToolApiService 正在释放资源\");\r\n _httpClient?.Dispose();\r\n }\r\n }\r\n\r\n public interface IToolApiService : IDisposable\r\n {\r\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<string> toolServices);\r\n }\r\n}"}, "0a160f150320c8d6c21c1866d32b00a31d6374a6": {"t": 1785760658.1865559, "n": 27361, "h": "c6fa8ce9deac3a34", "c": "// ToolApiService.cs - 专门处理工具调用\r\nusing DeepSeekChat.Models;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Serialization;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Net.Http;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing static DeepSeekChat.Services.ToolApiService;\r\n\r\nnamespace DeepSeekChat.Services\r\n{\r\n public class ToolApiService : IToolApiService\r\n {\r\n // 工具服务名称常量定义\r\n public static class ToolServices\r\n {\r\n public const string FileRead = \"FileRead\";\r\n public const string FileCreate = \"FileCreate\";\r\n public const string FileWrite = \"FileWrite\";\r\n public const string FileSystem = \"FileSystem\";\r\n public const string Compile = \"Compile\";\r\n public const string FolderBrowser = \"FolderBrowser\";\r\n public const string CommandLine = \"CommandLine\";\r\n }\r\n\r\n private readonly HttpClient _httpClient;\r\n private readonly string _logFilePath;\r\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\r\n\r\n public ToolApiService(string apiKey, string logFilePath = null)\r\n {\r\n _httpClient = new HttpClient();\r\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\r\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\r\n\r\n // 设置日志文件路径\r\n _logFilePath = logFilePath ?? GetDefaultLogFilePath();\r\n\r\n // 确保日志目录存在\r\n EnsureLogDirectory();\r\n\r\n LogInfo($\"ToolApiService 初始化完成,日志文件: {_logFilePath}\");\r\n }\r\n\r\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<string> toolServices)\r\n {\r\n LogInfo($\"开始处理工具调用请求\");\r\n LogInfo($\"传入的工具服务: {string.Join(\", \", toolServices ?? new List<string>())}\");\r\n LogInfo($\"对话历史消息数: {conversationHistory?.Count ?? 0}\");\r\n\r\n // 如果没有指定工具服务,返回空工具列表\r\n if (toolServices == null || toolServices.Count == 0)\r\n {\r\n LogInfo(\"未指定工具服务,使用空工具列表\");\r\n return await SendToolRequestAsync(conversationHistory, new List<object>());\r\n }\r\n\r\n // 收集所有选择的工具\r\n var allTools = new List<object>();\r\n\r\n foreach (var toolService in toolServices.Distinct())\r\n {\r\n var tools = GetToolsByService(toolService);\r\n if (tools != null)\r\n {\r\n allTools.AddRange(tools);\r\n LogInfo($\"添加工具服务 '{toolService}',包含 {tools.Count} 个工具\");\r\n }\r\n else\r\n {\r\n LogWarning($\"未找到工具服务: {toolService}\");\r\n }\r\n }\r\n\r\n LogInfo($\"总计添加 {allTools.Count} 个工具\");\r\n return await SendToolRequestAsync(conversationHistory, allTools);\r\n }\r\n\r\n // 私有方法:使用具体的工具列表发送请求\r\n private async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<object> tools)\r\n {\r\n LogInfo($\"开始发送API请求,工具数量: {tools.Count}\");\r\n\r\n var requestData = new ChatRequest\r\n {\r\n Messages = conversationHistory,\r\n MaxTokens = 5000,\r\n Temperature = 0.3,\r\n Tools = tools,\r\n ToolChoice = tools.Count > 0 ? \"auto\" : \"none\"\r\n };\r\n\r\n var settings = new JsonSerializerSettings\r\n {\r\n ContractResolver = new DefaultContractResolver\r\n {\r\n NamingStrategy = new SnakeCaseNamingStrategy()\r\n },\r\n NullValueHandling = NullValueHandling.Ignore,\r\n Formatting = Formatting.Indented // 为日志美化JSON\r\n };\r\n\r\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\r\n\r\n // 记录发送给API的请求内容\r\n LogRequest(jsonContent, tools.Count > 0);\r\n\r\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\r\n\r\n try\r\n {\r\n LogInfo($\"正在发送请求到 DeepSeek API: {ApiUrl}\");\r\n var response = await _httpClient.PostAsync(ApiUrl, content);\r\n\r\n LogInfo($\"收到API响应,状态码: {(int)response.StatusCode} {response.StatusCode}\");\r\n response.EnsureSuccessStatusCode();\r\n\r\n var responseJson = await response.Content.ReadAsStringAsync();\r\n\r\n // 记录API返回的内容\r\n LogResponse(responseJson, (int)response.StatusCode);\r\n\r\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\r\n\r\n var result = new ToolCallResponse();\r\n\r\n if (apiResponse?.Choices?.Count > 0)\r\n {\r\n var message = apiResponse.Choices[0].Message;\r\n\r\n result.Content = message.Content ?? string.Empty;\r\n result.ToolCalls = message.ToolCalls;\r\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\r\n result.Success = true;\r\n\r\n if (result.HasToolCalls)\r\n {\r\n // 提取工具调用信息\r\n result.ToolCalls = message.ToolCalls;\r\n LogInfo($\"API返回了 {message.ToolCalls.Count} 个工具调用\");\r\n for (int i = 0; i < message.ToolCalls.Count; i++)\r\n {\r\n var toolCall = message.ToolCalls[i];\r\n LogInfo($\"工具调用 #{i + 1}: {toolCall.Function?.Name}\");\r\n }\r\n }\r\n else\r\n {\r\n LogInfo($\"API返回了文本回复,长度: {result.Content.Length} 字符\");\r\n }\r\n }\r\n else\r\n {\r\n result.Content = \"没有生成有效的回复。\";\r\n result.Success = false;\r\n LogWarning(\"API返回了空响应或无有效选择\");\r\n }\r\n\r\n return result;\r\n }\r\n catch (HttpRequestException ex)\r\n {\r\n LogError($\"工具调用请求失败: {ex.Message}\");\r\n return new ToolCallResponse\r\n {\r\n Content = $\"工具调用请求失败: {ex.Message}\",\r\n Success = false\r\n };\r\n }\r\n catch (Exception ex)\r\n {\r\n LogError($\"处理API响应时发生错误: {ex.Message}\");\r\n return new ToolCallResponse\r\n {\r\n Content = $\"处理API响应时发生错误: {ex.Message}\",\r\n Success = false\r\n };\r\n }\r\n }\r\n\r\n // 根据服务名称获取对应的工具列表\r\n private List<object> GetToolsByService(string toolService)\r\n {\r\n LogDebug($\"获取工具服务: {toolService}\");\r\n return toolService switch\r\n {\r\n ToolServices.FileRead => GetFileReadTools(),\r\n ToolServices.FileCreate => GetFileCreateTools(),\r\n ToolServices.FileWrite => GetFileWriteTools(),\r\n ToolServices.FolderBrowser => GetFolderBrowserTools(),\r\n ToolServices.CommandLine => GetCommandLineTools(),\r\n _ => null\r\n };\r\n }\r\n\r\n public List<string> GetToolsNameByService(string toolService)\r\n {\r\n LogDebug($\"获取工具名称: {toolService}\");\r\n return toolService switch\r\n {\r\n ToolServices.FileRead => [\"read_file\"],\r\n ToolServices.FileCreate => [\"create_file\"],\r\n ToolServices.FileWrite => [\"write_file\"],\r\n ToolServices.FolderBrowser => [\"get_folder_structure_description\"],\r\n ToolServices.CommandLine => [\"execute_command\"],\r\n _ => [\"\"]\r\n };\r\n }\r\n\r\n #region 日志记录方法\r\n\r\n private string GetDefaultLogFilePath()\r\n {\r\n var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\r\n var logDir = Path.Combine(appDataPath, \"DeepSeekChat\", \"Logs\");\r\n var timestamp = DateTime.Now.ToString(\"yyyyMMdd\");\r\n return Path.Combine(logDir, $\"tool_api_{timestamp}.log\");\r\n }\r\n\r\n private void EnsureLogDirectory()\r\n {\r\n var logDir = Path.GetDirectoryName(_logFilePath);\r\n if (!string.IsNullOrEmpty(logDir) && !Directory.Exists(logDir))\r\n {\r\n Directory.CreateDirectory(logDir);\r\n }\r\n }\r\n\r\n private void LogRequest(string jsonContent, bool hasTools)\r\n {\r\n try\r\n {\r\n var logEntry = new StringBuilder();\r\n logEntry.AppendLine(\"=\".PadRight(80, '='));\r\n logEntry.AppendLine($\"请求时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\");\r\n logEntry.AppendLine($\"目标URL: {ApiUrl}\");\r\n logEntry.AppendLine($\"包含工具: {(hasTools ? \"是\" : \"否\")}\");\r\n logEntry.AppendLine(\"请求内容:\");\r\n logEntry.AppendLine(jsonContent);\r\n logEntry.AppendLine();\r\n\r\n File.AppendAllText(_logFilePath, logEntry.ToString());\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n private void LogResponse(string jsonContent, int statusCode)\r\n {\r\n try\r\n {\r\n var logEntry = new StringBuilder();\r\n logEntry.AppendLine($\"响应时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\");\r\n logEntry.AppendLine($\"状态码: {statusCode}\");\r\n logEntry.AppendLine(\"响应内容:\");\r\n\r\n // 尝试美化JSON响应\r\n try\r\n {\r\n var jsonObject = JsonConvert.DeserializeObject(jsonContent);\r\n var formattedJson = JsonConvert.SerializeObject(jsonObject, Formatting.Indented);\r\n logEntry.AppendLine(formattedJson);\r\n }\r\n catch\r\n {\r\n logEntry.AppendLine(jsonContent);\r\n }\r\n\r\n logEntry.AppendLine(\"=\".PadRight(80, '='));\r\n logEntry.AppendLine();\r\n\r\n File.AppendAllText(_logFilePath, logEntry.ToString());\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n private void LogInfo(string message)\r\n {\r\n WriteLog(\"INFO\", message);\r\n }\r\n\r\n private void LogWarning(string message)\r\n {\r\n WriteLog(\"WARN\", message);\r\n }\r\n\r\n private void LogError(string message)\r\n {\r\n WriteLog(\"ERROR\", message);\r\n }\r\n\r\n private void LogDebug(string message)\r\n {\r\n WriteLog(\"DEBUG\", message);\r\n }\r\n\r\n private void WriteLog(string level, string message)\r\n {\r\n try\r\n {\r\n var timestamp = DateTime.Now.ToString(\"yyyy-MM-dd HH:mm:ss.fff\");\r\n var logMessage = $\"{timestamp} [{level}] {message}\";\r\n\r\n File.AppendAllText(_logFilePath, logMessage + Environment.NewLine);\r\n\r\n // 同时输出到控制台以便调试\r\n Console.WriteLine(logMessage);\r\n }\r\n catch (Exception ex)\r\n {\r\n Console.WriteLine($\"无法写入日志文件: {ex.Message}\");\r\n }\r\n }\r\n\r\n #endregion\r\n\r\n #region 工具定义方法\r\n\r\n private List<object> GetFolderInfoTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"get_folder_info\",\r\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n folder_path = new\r\n {\r\n type = \"string\",\r\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\r\n }\r\n },\r\n required = new[] { \"folder_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> DeleteFileTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"delete_file\",\r\n description = \"删除文件\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileCreateTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"create_file\",\r\n description = \"创建新文件\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\r\n },\r\n content = new\r\n {\r\n type = \"string\",\r\n description = \"文件内容,默认为空字符串\"\r\n },\r\n overwrite = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否覆盖已存在的文件,默认为false\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileReadTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"read_file\",\r\n description = \"读取文件内容\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\r\n },\r\n encoding = new\r\n {\r\n type = \"string\",\r\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\r\n },\r\n max_size_bytes = new\r\n {\r\n type = \"integer\",\r\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\r\n }\r\n },\r\n required = new[] { \"file_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFileWriteTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"write_file\",\r\n description = \"写入文件内容\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n file_path = new\r\n {\r\n type = \"string\",\r\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\r\n },\r\n content = new\r\n {\r\n type = \"string\",\r\n description = \"要写入的文件内容\"\r\n },\r\n encoding = new\r\n {\r\n type = \"string\",\r\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\r\n },\r\n mode = new\r\n {\r\n type = \"string\",\r\n description = \"写入模式:create=仅创建新文件(文件存在则失败)、overwrite=覆盖文件(文件存在则覆盖)、append=在文件末尾追加内容,默认为create\",\r\n @enum = new[] { \"create\", \"overwrite\", \"append\" }\r\n }\r\n },\r\n required = new[] { \"file_path\", \"content\" ,\"mode\"}\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetFolderBrowserTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"get_folder_structure_description\",\r\n description = \"浏览本地文件夹,获取文件列表,可以过滤特定文件类型并排除指定文件夹\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n folder_path = new\r\n {\r\n type = \"string\",\r\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\r\n },\r\n include_subdirectories = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否包含子目录,默认为false\"\r\n },\r\n filter_extensions = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"要过滤的文件扩展名列表,如:['.txt', '.pdf', '.jpg'],只返回这些扩展名的文件\"\r\n },\r\n exclude_paths = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"要排除的文件夹名称或路径列表,如:['node_modules', 'bin', 'obj', '.git']。这些文件夹及其所有内容将被忽略\"\r\n }\r\n },\r\n required = new[] { \"folder_path\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n private List<object> GetCompileTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"compile_csharp\",\r\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n code = new\r\n {\r\n type = \"string\",\r\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\r\n },\r\n outputPath = new\r\n {\r\n type = \"string\",\r\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\r\n },\r\n execute = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\r\n },\r\n includeReferences = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\r\n },\r\n targetFramework = new\r\n {\r\n type = \"string\",\r\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\r\n }\r\n },\r\n required = new[] { \"code\" }\r\n }\r\n }\r\n },\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"compile_and_execute\",\r\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n code = new\r\n {\r\n type = \"string\",\r\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\r\n },\r\n output_path = new\r\n {\r\n type = \"string\",\r\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\r\n },\r\n arguments = new\r\n {\r\n type = \"array\",\r\n items = new { type = \"string\" },\r\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\r\n }\r\n },\r\n required = new[] { \"code\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n\r\n private List<object> GetCommandLineTools()\r\n {\r\n return new List<object>\r\n {\r\n new\r\n {\r\n type = \"function\",\r\n function = new\r\n {\r\n name = \"execute_command\",\r\n description = \"执行Windows命令行程序或系统命令。注意:show_window参数只在wait_for_exit为false时有效,当wait_for_exit为true时始终不显示窗口\",\r\n parameters = new\r\n {\r\n type = \"object\",\r\n properties = new\r\n {\r\n command = new\r\n {\r\n type = \"string\",\r\n description = \"要执行的命令,如:ipconfig、dir、ping 127.0.0.1 等\"\r\n },\r\n arguments = new\r\n {\r\n type = \"string\",\r\n description = \"命令行参数,默认为空字符串\"\r\n },\r\n working_directory = new\r\n {\r\n type = \"string\",\r\n description = \"命令执行的工作目录,默认为当前程序运行目录\"\r\n },\r\n wait_for_exit = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否等待命令执行完成并获取输出。如果为true,则必须等待执行完成并捕获输出信息(此时不会显示控制台窗口);如果为false,则立即返回进程ID(此时可以通过show_window控制是否显示窗口)\",\r\n @default = true\r\n },\r\n timeout_seconds = new\r\n {\r\n type = \"number\",\r\n description = \"命令执行超时时间(秒),默认为300秒。仅在wait_for_exit为true时有效\",\r\n @default = 300\r\n },\r\n show_window = new\r\n {\r\n type = \"boolean\",\r\n description = \"是否显示控制台窗口。仅在wait_for_exit为false时有效:为true时显示窗口,为false时后台运行。当wait_for_exit为true时此参数无效,始终不显示窗口\",\r\n @default = false\r\n }\r\n },\r\n required = new[] { \"command\" }\r\n }\r\n }\r\n }\r\n };\r\n }\r\n #endregion\r\n\r\n public void Dispose()\r\n {\r\n LogInfo(\"ToolApiService 正在释放资源\");\r\n _httpClient?.Dispose();\r\n }\r\n }\r\n\r\n public interface IToolApiService : IDisposable\r\n {\r\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, List<string> toolServices);\r\n }\r\n}"}, "1e2abe5d1fdf6b45407cf666aeb86165754be59a": {"t": 1785760659.7249336, "n": 28414, "h": "2be8306f19368bd6", "c": "// ToolApiService.cs - 专门处理工具调用\nusing DeepSeekChat.Models;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\nusing static DeepSeekChat.Services.ToolApiService;\n\nnamespace DeepSeekChat.Services\n{\n public class ToolApiService : IToolApiService\n {\n public enum ToolApiType\n {\n FileRead,\n FileCreate,\n FileWrite,\n FileSystem,\n TaskManager,\n Compile,\n UserNeedsAnalysis\n }\n private readonly HttpClient _httpClient;\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\n\n public ToolApiService(string apiKey)\n {\n _httpClient = new HttpClient();\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\n }\n\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType)\n {\n List<Object> tools = new List<Object>();\n switch(toolApiType)\n {\n case ToolApiType.FileRead:\n tools = GetFileReadTools();\n break;\n case ToolApiType.FileCreate:\n tools = GetFileCreateTools();\n break;\n case ToolApiType.FileWrite:\n tools = GetFileWriteTools();\n break;\n case ToolApiType.TaskManager:\n tools = GetTaskCreationTools();\n break;\n case ToolApiType.Compile:\n tools = GetCompileTools();\n break;\n case ToolApiType.UserNeedsAnalysis:\n tools = GetHandleUserNeedsTools();\n break;\n default:\n break;\n }\n var requestData = new ChatRequest\n {\n Messages = conversationHistory,\n MaxTokens = 5000,\n Temperature = 0.3,\n Tools = tools,\n ToolChoice = \"auto\"\n };\n\n var settings = new JsonSerializerSettings\n {\n ContractResolver = new DefaultContractResolver\n {\n NamingStrategy = new SnakeCaseNamingStrategy()\n },\n NullValueHandling = NullValueHandling.Ignore\n };\n\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\n\n try\n {\n var response = await _httpClient.PostAsync(ApiUrl, content);\n response.EnsureSuccessStatusCode();\n\n var responseJson = await response.Content.ReadAsStringAsync();\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\n\n var result = new ToolCallResponse();\n\n if (apiResponse?.Choices?.Count > 0)\n {\n var message = apiResponse.Choices[0].Message;\n\n result.Content = message.Content ?? string.Empty;\n result.ToolCalls = message.ToolCalls;\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\n result.Success = true;\n\n if (result.HasToolCalls)\n {\n // 提取工具调用信息\n result.ToolCallInfos = ExtractToolCallInfos(message.ToolCalls);\n }\n }\n else\n {\n result.Content = \"没有生成有效的回复。\";\n result.Success = false;\n }\n\n return result;\n }\n catch (HttpRequestException ex)\n {\n return new ToolCallResponse\n {\n Content = $\"工具调用请求失败: {ex.Message}\",\n Success = false\n };\n }\n }\n\n private List<ToolCallInfo> ExtractToolCallInfos(List<ToolCall> toolCalls)\n {\n var infos = new List<ToolCallInfo>();\n\n foreach (var toolCall in toolCalls)\n {\n var info = new ToolCallInfo\n {\n Id = toolCall.Id,\n FunctionName = toolCall.Function?.Name,\n Arguments = toolCall.Function?.Arguments\n };\n infos.Add(info);\n }\n\n return infos;\n }\n\n private List<object> GetFileSystemTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"browse_local_folder\",\n description = \"浏览本地文件夹,获取文件列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n },\n include_subdirectories = new\n {\n type = \"boolean\",\n description = \"是否包含子目录,默认为false\"\n },\n filter_extensions = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"要过滤的文件扩展名列表,如:[.txt, .pdf, .jpg]\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"get_folder_info\",\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"delete_file\",\n description = \"删除文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"write_file\",\n description = \"写入文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"要写入的文件内容\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n append = new\n {\n type = \"boolean\",\n description = \"是否以追加模式写入,true=在文件末尾追加内容,false=覆盖整个文件,默认为false\"\n }\n },\n required = new[] { \"file_path\", \"content\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileCreateTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileReadTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileWriteTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"write_file\",\n description = \"写入文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"要写入的文件内容\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n mode = new\n {\n type = \"string\",\n description = \"写入模式:create=仅创建新文件(文件存在则失败)、overwrite=覆盖文件(文件存在则覆盖)、append=在文件末尾追加内容,默认为create\",\n @enum = new[] { \"create\", \"overwrite\", \"append\" }\n }\n },\n required = new[] { \"file_path\", \"content\" ,\"mode\"}\n }\n }\n }\n };\n }\n \n private List<object> GetCompileTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_csharp\",\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\n },\n outputPath = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\n },\n execute = new\n {\n type = \"boolean\",\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\n },\n includeReferences = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\n },\n targetFramework = new\n {\n type = \"string\",\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_and_execute\",\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\n },\n output_path = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\n },\n arguments = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n }\n };\n }\n\n\n public List<object> GetTaskCreationTools()\n {\n // 构建 TaskDefinition 的 JSON Schema 属性\n var taskDefinitionProperties = new Dictionary<string, object>\n {\n [\"task_id\"] = new\n {\n type = \"string\",\n description = \"任务ID,建议使用GUID\"\n },\n [\"project_name\"] = new\n {\n type = \"string\",\n description = \"工程文件名\"\n },\n [\"file_name\"] = new\n {\n type = \"string\",\n description = \"文件名\"\n },\n [\"file_path\"] = new\n {\n type = \"string\",\n description = \"文件路径\"\n },\n [\"function\"] = new\n {\n type = \"string\",\n description = \"文件功能描述\"\n },\n [\"dependencies\"] = new\n {\n type = \"array\",\n description = \"依赖的文件名列表\",\n items = new { type = \"string\" }\n },\n [\"requirements\"] = new\n {\n type = \"string\",\n description = \"创建该文件的具体要求\"\n },\n [\"estimated_complexity\"] = new\n {\n type = \"string\",\n description = \"估计复杂度\",\n @enum = new[] { \"low\", \"medium\", \"high\" }\n },\n [\"technology_requirements\"] = new\n {\n type = \"array\",\n description = \"技术需求列表\",\n items = new { type = \"string\" }\n },\n [\"testing_requirements\"] = new\n {\n type = \"string\",\n description = \"测试要求\"\n },\n [\"status\"] = new\n {\n type = \"string\",\n description = \"任务状态\",\n @default = \"pending\"\n },\n [\"assigned_to\"] = new\n {\n type = \"string\",\n description = \"分配给谁\",\n @default = \"CodingAgent\"\n }\n };\n\n // 任务定义所需的必填字段\n var requiredTaskProperties = new[] {\n \"task_id\",\"project_name\", \"file_name\", \"file_path\", \"function\", \"requirements\"\n };\n\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"distribute_coding_tasks\",\n description = \"将文件创建和编码任务分配给CodingAgent,输入为任务列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n tasks = new\n {\n type = \"array\",\n description = \"任务定义列表,符合TaskDefinition格式\",\n items = new\n {\n type = \"object\",\n properties = taskDefinitionProperties,\n required = requiredTaskProperties\n }\n },\n distribution_strategy = new\n {\n type = \"string\",\n description = \"分配策略\",\n @enum = new[] {\n \"sequential_by_dependency\",\n \"parallel_when_possible\",\n \"complexity_based\",\n \"priority_based\"\n },\n @default = \"sequential_by_dependency\"\n },\n execution_order = new\n {\n type = \"boolean\",\n description = \"是否返回任务执行顺序建议\",\n @default = true\n },\n batch_size = new\n {\n type = \"integer\",\n description = \"批次大小,一次分配多少个任务给CodingAgent\",\n @default = 1,\n minimum = 1,\n maximum = 10\n }\n },\n required = new[] { \"tasks\" }\n }\n }\n }\n };\n }\n\n public List<object> GetHandleUserNeedsTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"send_to_agent\",\n description = \"路由用户请求:需求分析 -> RequirementAnalysisAgent,代码审查 -> ReviewAgent,其他 -> 返回User并说明原因\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n agent_name = new\n {\n type = \"string\",\n description = \"目标接收方:'RequirementAnalysisAgent'、'ReviewAgent'或'User'\",\n @enum = new[] {\n \"RequirementAnalysisAgent\",\n \"ReviewAgent\",\n \"User\"\n }\n },\n to_agent_content = new\n {\n type = \"string\",\n description = \"发送给接收方的消息内容\"\n }\n },\n required = new[] { \"agent_name\", \"to_agent_content\" }\n }\n }\n }\n };\n }\n\n public void Dispose()\n {\n _httpClient?.Dispose();\n }\n }\n\n public interface IToolApiService : IDisposable\n {\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType);\n }\n\n // 工具调用响应模型\n public class ToolCallResponse\n {\n public string Content { get; set; }\n public List<ToolCall> ToolCalls { get; set; }\n public List<ToolCallInfo> ToolCallInfos { get; set; }\n public bool HasToolCalls { get; set; }\n public bool Success { get; set; }\n }\n\n public class ToolCallInfo\n {\n public string Id { get; set; }\n public string FunctionName { get; set; }\n public string Arguments { get; set; }\n }\n}"}, "9ba98cb9c72c380d85bcd66580947b9be8b33bda": {"t": 1785760661.3131373, "n": 26344, "h": "a974d0f8bf165b8d", "c": "// ToolApiService.cs - 专门处理工具调用\nusing DeepSeekChat.Models;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\nusing static DeepSeekChat.Services.ToolApiService;\n\nnamespace DeepSeekChat.Services\n{\n public class ToolApiService : IToolApiService\n {\n public enum ToolApiType\n {\n FileRead,\n FileCreate,\n FileSystem,\n TaskManager,\n Compile,\n UserNeedsAnalysis\n }\n private readonly HttpClient _httpClient;\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\n\n public ToolApiService(string apiKey)\n {\n _httpClient = new HttpClient();\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\n }\n\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType)\n {\n List<Object> tools = new List<Object>();\n switch(toolApiType)\n {\n case ToolApiType.FileRead:\n tools = GetFileReadTools();\n break;\n case ToolApiType.FileCreate:\n tools = GetFileCreateTools();\n break;\n case ToolApiType.TaskManager:\n tools = GetTaskCreationTools();\n break;\n case ToolApiType.Compile:\n tools = GetCompileTools();\n break;\n case ToolApiType.UserNeedsAnalysis:\n tools = GetHandleUserNeedsTools();\n break;\n default:\n break;\n }\n var requestData = new ChatRequest\n {\n Messages = conversationHistory,\n MaxTokens = 5000,\n Temperature = 0.3,\n Tools = tools,\n ToolChoice = \"auto\"\n };\n\n var settings = new JsonSerializerSettings\n {\n ContractResolver = new DefaultContractResolver\n {\n NamingStrategy = new SnakeCaseNamingStrategy()\n },\n NullValueHandling = NullValueHandling.Ignore\n };\n\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\n\n try\n {\n var response = await _httpClient.PostAsync(ApiUrl, content);\n response.EnsureSuccessStatusCode();\n\n var responseJson = await response.Content.ReadAsStringAsync();\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\n\n var result = new ToolCallResponse();\n\n if (apiResponse?.Choices?.Count > 0)\n {\n var message = apiResponse.Choices[0].Message;\n\n result.Content = message.Content ?? string.Empty;\n result.ToolCalls = message.ToolCalls;\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\n result.Success = true;\n\n if (result.HasToolCalls)\n {\n // 提取工具调用信息\n result.ToolCallInfos = ExtractToolCallInfos(message.ToolCalls);\n }\n }\n else\n {\n result.Content = \"没有生成有效的回复。\";\n result.Success = false;\n }\n\n return result;\n }\n catch (HttpRequestException ex)\n {\n return new ToolCallResponse\n {\n Content = $\"工具调用请求失败: {ex.Message}\",\n Success = false\n };\n }\n }\n\n private List<ToolCallInfo> ExtractToolCallInfos(List<ToolCall> toolCalls)\n {\n var infos = new List<ToolCallInfo>();\n\n foreach (var toolCall in toolCalls)\n {\n var info = new ToolCallInfo\n {\n Id = toolCall.Id,\n FunctionName = toolCall.Function?.Name,\n Arguments = toolCall.Function?.Arguments\n };\n infos.Add(info);\n }\n\n return infos;\n }\n\n private List<object> GetFileSystemTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"browse_local_folder\",\n description = \"浏览本地文件夹,获取文件列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n },\n include_subdirectories = new\n {\n type = \"boolean\",\n description = \"是否包含子目录,默认为false\"\n },\n filter_extensions = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"要过滤的文件扩展名列表,如:[.txt, .pdf, .jpg]\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"get_folder_info\",\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"delete_file\",\n description = \"删除文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"write_file\",\n description = \"写入文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"要写入的文件内容\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n append = new\n {\n type = \"boolean\",\n description = \"是否以追加模式写入,true=在文件末尾追加内容,false=覆盖整个文件,默认为false\"\n }\n },\n required = new[] { \"file_path\", \"content\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileCreateTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileReadTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetCompileTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_csharp\",\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\n },\n outputPath = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\n },\n execute = new\n {\n type = \"boolean\",\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\n },\n includeReferences = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\n },\n targetFramework = new\n {\n type = \"string\",\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_and_execute\",\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\n },\n output_path = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\n },\n arguments = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n }\n };\n }\n\n\n public List<object> GetTaskCreationTools()\n {\n // 构建 TaskDefinition 的 JSON Schema 属性\n var taskDefinitionProperties = new Dictionary<string, object>\n {\n [\"task_id\"] = new\n {\n type = \"string\",\n description = \"任务ID,建议使用GUID\"\n },\n [\"project_name\"] = new\n {\n type = \"string\",\n description = \"工程文件名\"\n },\n [\"file_name\"] = new\n {\n type = \"string\",\n description = \"文件名\"\n },\n [\"file_path\"] = new\n {\n type = \"string\",\n description = \"文件路径\"\n },\n [\"function\"] = new\n {\n type = \"string\",\n description = \"文件功能描述\"\n },\n [\"dependencies\"] = new\n {\n type = \"array\",\n description = \"依赖的文件名列表\",\n items = new { type = \"string\" }\n },\n [\"requirements\"] = new\n {\n type = \"string\",\n description = \"创建该文件的具体要求\"\n },\n [\"estimated_complexity\"] = new\n {\n type = \"string\",\n description = \"估计复杂度\",\n @enum = new[] { \"low\", \"medium\", \"high\" }\n },\n [\"technology_requirements\"] = new\n {\n type = \"array\",\n description = \"技术需求列表\",\n items = new { type = \"string\" }\n },\n [\"testing_requirements\"] = new\n {\n type = \"string\",\n description = \"测试要求\"\n },\n [\"status\"] = new\n {\n type = \"string\",\n description = \"任务状态\",\n @default = \"pending\"\n },\n [\"assigned_to\"] = new\n {\n type = \"string\",\n description = \"分配给谁\",\n @default = \"CodingAgent\"\n }\n };\n\n // 任务定义所需的必填字段\n var requiredTaskProperties = new[] {\n \"task_id\",\"project_name\", \"file_name\", \"file_path\", \"function\", \"requirements\"\n };\n\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"distribute_coding_tasks\",\n description = \"将文件创建和编码任务分配给CodingAgent,输入为任务列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n tasks = new\n {\n type = \"array\",\n description = \"任务定义列表,符合TaskDefinition格式\",\n items = new\n {\n type = \"object\",\n properties = taskDefinitionProperties,\n required = requiredTaskProperties\n }\n },\n distribution_strategy = new\n {\n type = \"string\",\n description = \"分配策略\",\n @enum = new[] {\n \"sequential_by_dependency\",\n \"parallel_when_possible\",\n \"complexity_based\",\n \"priority_based\"\n },\n @default = \"sequential_by_dependency\"\n },\n execution_order = new\n {\n type = \"boolean\",\n description = \"是否返回任务执行顺序建议\",\n @default = true\n },\n batch_size = new\n {\n type = \"integer\",\n description = \"批次大小,一次分配多少个任务给CodingAgent\",\n @default = 1,\n minimum = 1,\n maximum = 10\n }\n },\n required = new[] { \"tasks\" }\n }\n }\n }\n };\n }\n\n public List<object> GetHandleUserNeedsTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"send_to_agent\",\n description = \"路由用户请求:需求分析 -> RequirementAnalysisAgent,代码审查 -> ReviewAgent,其他 -> 返回User并说明原因\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n agent_name = new\n {\n type = \"string\",\n description = \"目标接收方:'RequirementAnalysisAgent'、'ReviewAgent'或'User'\",\n @enum = new[] {\n \"RequirementAnalysisAgent\",\n \"ReviewAgent\",\n \"User\"\n }\n },\n to_agent_content = new\n {\n type = \"string\",\n description = \"发送给接收方的消息内容\"\n }\n },\n required = new[] { \"agent_name\", \"to_agent_content\" }\n }\n }\n }\n };\n }\n\n public void Dispose()\n {\n _httpClient?.Dispose();\n }\n }\n\n public interface IToolApiService : IDisposable\n {\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType);\n }\n\n // 工具调用响应模型\n public class ToolCallResponse\n {\n public string Content { get; set; }\n public List<ToolCall> ToolCalls { get; set; }\n public List<ToolCallInfo> ToolCallInfos { get; set; }\n public bool HasToolCalls { get; set; }\n public bool Success { get; set; }\n }\n\n public class ToolCallInfo\n {\n public string Id { get; set; }\n public string FunctionName { get; set; }\n public string Arguments { get; set; }\n }\n}"}, "b22720737b876cc8ec2cab71396420391840c56b": {"t": 1785760663.1681724, "n": 24865, "h": "f0265e9fac3a74b1", "c": "// ToolApiService.cs - 专门处理工具调用\nusing DeepSeekChat.Models;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\nusing static DeepSeekChat.Services.ToolApiService;\n\nnamespace DeepSeekChat.Services\n{\n public class ToolApiService : IToolApiService\n {\n public enum ToolApiType\n {\n FileRead,\n FileCreate,\n FileSystem,\n TaskManager,\n Compile\n }\n private readonly HttpClient _httpClient;\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\n\n public ToolApiService(string apiKey)\n {\n _httpClient = new HttpClient();\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\n }\n\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType)\n {\n List<Object> tools = new List<Object>();\n switch(toolApiType)\n {\n case ToolApiType.FileRead:\n tools = GetFileReadTools();\n break;\n case ToolApiType.FileCreate:\n tools = GetFileCreateTools();\n break;\n case ToolApiType.TaskManager:\n tools = GetTaskCreationTools();\n break;\n case ToolApiType.Compile:\n tools = GetCompileTools();\n break;\n default:\n break;\n }\n var requestData = new ChatRequest\n {\n Messages = conversationHistory,\n MaxTokens = 5000,\n Temperature = 0.3,\n Tools = tools,\n ToolChoice = \"auto\"\n };\n\n var settings = new JsonSerializerSettings\n {\n ContractResolver = new DefaultContractResolver\n {\n NamingStrategy = new SnakeCaseNamingStrategy()\n },\n NullValueHandling = NullValueHandling.Ignore\n };\n\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\n\n try\n {\n var response = await _httpClient.PostAsync(ApiUrl, content);\n response.EnsureSuccessStatusCode();\n\n var responseJson = await response.Content.ReadAsStringAsync();\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\n\n var result = new ToolCallResponse();\n\n if (apiResponse?.Choices?.Count > 0)\n {\n var message = apiResponse.Choices[0].Message;\n\n result.Content = message.Content ?? string.Empty;\n result.ToolCalls = message.ToolCalls;\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\n result.Success = true;\n\n if (result.HasToolCalls)\n {\n // 提取工具调用信息\n result.ToolCallInfos = ExtractToolCallInfos(message.ToolCalls);\n }\n }\n else\n {\n result.Content = \"没有生成有效的回复。\";\n result.Success = false;\n }\n\n return result;\n }\n catch (HttpRequestException ex)\n {\n return new ToolCallResponse\n {\n Content = $\"工具调用请求失败: {ex.Message}\",\n Success = false\n };\n }\n }\n\n private List<ToolCallInfo> ExtractToolCallInfos(List<ToolCall> toolCalls)\n {\n var infos = new List<ToolCallInfo>();\n\n foreach (var toolCall in toolCalls)\n {\n var info = new ToolCallInfo\n {\n Id = toolCall.Id,\n FunctionName = toolCall.Function?.Name,\n Arguments = toolCall.Function?.Arguments\n };\n infos.Add(info);\n }\n\n return infos;\n }\n\n private List<object> GetFileSystemTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"browse_local_folder\",\n description = \"浏览本地文件夹,获取文件列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n },\n include_subdirectories = new\n {\n type = \"boolean\",\n description = \"是否包含子目录,默认为false\"\n },\n filter_extensions = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"要过滤的文件扩展名列表,如:[.txt, .pdf, .jpg]\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"get_folder_info\",\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"delete_file\",\n description = \"删除文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"write_file\",\n description = \"写入文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"要写入的文件内容\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n append = new\n {\n type = \"boolean\",\n description = \"是否以追加模式写入,true=在文件末尾追加内容,false=覆盖整个文件,默认为false\"\n }\n },\n required = new[] { \"file_path\", \"content\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileCreateTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileReadTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetCompileTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_csharp\",\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\n },\n outputPath = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\n },\n execute = new\n {\n type = \"boolean\",\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\n },\n includeReferences = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\n },\n targetFramework = new\n {\n type = \"string\",\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_and_execute\",\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\n },\n output_path = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\n },\n arguments = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n }\n };\n }\n\n\n public List<object> GetTaskCreationTools()\n {\n // 构建 TaskDefinition 的 JSON Schema 属性\n var taskDefinitionProperties = new Dictionary<string, object>\n {\n [\"task_id\"] = new\n {\n type = \"string\",\n description = \"任务ID,建议使用GUID\"\n },\n [\"project_name\"] = new\n {\n type = \"string\",\n description = \"工程文件名\"\n },\n [\"file_name\"] = new\n {\n type = \"string\",\n description = \"文件名\"\n },\n [\"file_path\"] = new\n {\n type = \"string\",\n description = \"文件路径\"\n },\n [\"function\"] = new\n {\n type = \"string\",\n description = \"文件功能描述\"\n },\n [\"dependencies\"] = new\n {\n type = \"array\",\n description = \"依赖的文件名列表\",\n items = new { type = \"string\" }\n },\n [\"requirements\"] = new\n {\n type = \"string\",\n description = \"创建该文件的具体要求\"\n },\n [\"estimated_complexity\"] = new\n {\n type = \"string\",\n description = \"估计复杂度\",\n @enum = new[] { \"low\", \"medium\", \"high\" }\n },\n [\"technology_requirements\"] = new\n {\n type = \"array\",\n description = \"技术需求列表\",\n items = new { type = \"string\" }\n },\n [\"testing_requirements\"] = new\n {\n type = \"string\",\n description = \"测试要求\"\n },\n [\"status\"] = new\n {\n type = \"string\",\n description = \"任务状态\",\n @default = \"pending\"\n },\n [\"assigned_to\"] = new\n {\n type = \"string\",\n description = \"分配给谁\",\n @default = \"CodingAgent\"\n }\n };\n\n // 任务定义所需的必填字段\n var requiredTaskProperties = new[] {\n \"task_id\",\"project_name\", \"file_name\", \"file_path\", \"function\", \"requirements\"\n };\n\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"distribute_coding_tasks\",\n description = \"将文件创建和编码任务分配给CodingAgent,输入为任务列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n tasks = new\n {\n type = \"array\",\n description = \"任务定义列表,符合TaskDefinition格式\",\n items = new\n {\n type = \"object\",\n properties = taskDefinitionProperties,\n required = requiredTaskProperties\n }\n },\n distribution_strategy = new\n {\n type = \"string\",\n description = \"分配策略\",\n @enum = new[] {\n \"sequential_by_dependency\",\n \"parallel_when_possible\",\n \"complexity_based\",\n \"priority_based\"\n },\n @default = \"sequential_by_dependency\"\n },\n execution_order = new\n {\n type = \"boolean\",\n description = \"是否返回任务执行顺序建议\",\n @default = true\n },\n batch_size = new\n {\n type = \"integer\",\n description = \"批次大小,一次分配多少个任务给CodingAgent\",\n @default = 1,\n minimum = 1,\n maximum = 10\n }\n },\n required = new[] { \"tasks\" }\n }\n }\n }\n };\n }\n public void Dispose()\n {\n _httpClient?.Dispose();\n }\n }\n\n public interface IToolApiService : IDisposable\n {\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType);\n }\n\n // 工具调用响应模型\n public class ToolCallResponse\n {\n public string Content { get; set; }\n public List<ToolCall> ToolCalls { get; set; }\n public List<ToolCallInfo> ToolCallInfos { get; set; }\n public bool HasToolCalls { get; set; }\n public bool Success { get; set; }\n }\n\n public class ToolCallInfo\n {\n public string Id { get; set; }\n public string FunctionName { get; set; }\n public string Arguments { get; set; }\n }\n}"}, "37466c9f24e16ff187981cbd9db9f846c2ca02d4": {"t": 1785760664.7319808, "n": 24865, "h": "00529c6f2978cc15", "c": "// ToolApiService.cs - 专门处理工具调用\nusing DeepSeekChat.Models;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\nusing static DeepSeekChat.Services.ToolApiService;\n\nnamespace DeepSeekChat.Services\n{\n public class ToolApiService : IToolApiService\n {\n public enum ToolApiType\n {\n FileRead,\n FileCreate,\n FileSystem,\n TaskManager,\n Compile\n }\n private readonly HttpClient _httpClient;\n private const string ApiUrl = \"https://api.deepseek.com/chat/completions\";\n\n public ToolApiService(string apiKey)\n {\n _httpClient = new HttpClient();\n _httpClient.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n _httpClient.DefaultRequestHeaders.Add(\"Accept\", \"application/json\");\n }\n\n public async Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType)\n {\n List<Object> tools = new List<Object>();\n switch(toolApiType)\n {\n case ToolApiType.FileRead:\n tools = GetFileReadTools();\n break;\n case ToolApiType.FileCreate:\n tools = GetFileCreateTools();\n break;\n case ToolApiType.TaskManager:\n tools = GetTaskCreationTools();\n break;\n case ToolApiType.Compile:\n tools = GetCompileTools();\n break;\n default:\n break;\n }\n var requestData = new ChatRequest\n {\n Messages = conversationHistory,\n MaxTokens = 5000,\n Temperature = 0.7,\n Tools = tools,\n ToolChoice = \"auto\"\n };\n\n var settings = new JsonSerializerSettings\n {\n ContractResolver = new DefaultContractResolver\n {\n NamingStrategy = new SnakeCaseNamingStrategy()\n },\n NullValueHandling = NullValueHandling.Ignore\n };\n\n var jsonContent = JsonConvert.SerializeObject(requestData, settings);\n var content = new StringContent(jsonContent, Encoding.UTF8, \"application/json\");\n\n try\n {\n var response = await _httpClient.PostAsync(ApiUrl, content);\n response.EnsureSuccessStatusCode();\n\n var responseJson = await response.Content.ReadAsStringAsync();\n var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(responseJson);\n\n var result = new ToolCallResponse();\n\n if (apiResponse?.Choices?.Count > 0)\n {\n var message = apiResponse.Choices[0].Message;\n\n result.Content = message.Content ?? string.Empty;\n result.ToolCalls = message.ToolCalls;\n result.HasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;\n result.Success = true;\n\n if (result.HasToolCalls)\n {\n // 提取工具调用信息\n result.ToolCallInfos = ExtractToolCallInfos(message.ToolCalls);\n }\n }\n else\n {\n result.Content = \"没有生成有效的回复。\";\n result.Success = false;\n }\n\n return result;\n }\n catch (HttpRequestException ex)\n {\n return new ToolCallResponse\n {\n Content = $\"工具调用请求失败: {ex.Message}\",\n Success = false\n };\n }\n }\n\n private List<ToolCallInfo> ExtractToolCallInfos(List<ToolCall> toolCalls)\n {\n var infos = new List<ToolCallInfo>();\n\n foreach (var toolCall in toolCalls)\n {\n var info = new ToolCallInfo\n {\n Id = toolCall.Id,\n FunctionName = toolCall.Function?.Name,\n Arguments = toolCall.Function?.Arguments\n };\n infos.Add(info);\n }\n\n return infos;\n }\n\n private List<object> GetFileSystemTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"browse_local_folder\",\n description = \"浏览本地文件夹,获取文件列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要浏览的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n },\n include_subdirectories = new\n {\n type = \"boolean\",\n description = \"是否包含子目录,默认为false\"\n },\n filter_extensions = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"要过滤的文件扩展名列表,如:[.txt, .pdf, .jpg]\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"get_folder_info\",\n description = \"获取文件夹详细信息,包括文件数量、大小统计、常见文件类型等\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n folder_path = new\n {\n type = \"string\",\n description = \"要获取信息的文件夹完整路径,如:C:\\\\Users\\\\Username\\\\Documents\"\n }\n },\n required = new[] { \"folder_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"delete_file\",\n description = \"删除文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要删除的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_delete.txt\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"write_file\",\n description = \"写入文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要写入的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_write.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"要写入的文件内容\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n append = new\n {\n type = \"boolean\",\n description = \"是否以追加模式写入,true=在文件末尾追加内容,false=覆盖整个文件,默认为false\"\n }\n },\n required = new[] { \"file_path\", \"content\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileCreateTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"create_file\",\n description = \"创建新文件\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要创建的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\new_file.txt\"\n },\n content = new\n {\n type = \"string\",\n description = \"文件内容,默认为空字符串\"\n },\n overwrite = new\n {\n type = \"boolean\",\n description = \"是否覆盖已存在的文件,默认为false\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetFileReadTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"read_file\",\n description = \"读取文件内容\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n file_path = new\n {\n type = \"string\",\n description = \"要读取的文件完整路径,如:C:\\\\Users\\\\Username\\\\Documents\\\\file_to_read.txt\"\n },\n encoding = new\n {\n type = \"string\",\n description = \"文件编码格式,支持:UTF-8、ASCII、Unicode、UTF-32、UTF-7、BigEndianUnicode、Default,默认为UTF-8\"\n },\n max_size_bytes = new\n {\n type = \"integer\",\n description = \"最大读取文件大小(字节),超过此大小的文件将不会被读取,默认为10MB (10485760字节)\"\n }\n },\n required = new[] { \"file_path\" }\n }\n }\n }\n };\n }\n\n private List<object> GetCompileTools()\n {\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_csharp\",\n description = \"编译C#代码并可选地执行程序。支持生成可执行文件或在内存中直接执行代码。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译的C#源代码。必须是完整的程序,包含Main方法或入口点。\"\n },\n outputPath = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译。示例:C:\\\\Output\\\\Program.dll\"\n },\n execute = new\n {\n type = \"boolean\",\n description = \"是否在编译后立即执行程序。如果为true,将在内存中执行并返回执行结果。默认为false。\"\n },\n includeReferences = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"额外的程序集引用路径列表。示例:[\\\"Newtonsoft.Json.dll\\\", \\\"System.Data.dll\\\"]\"\n },\n targetFramework = new\n {\n type = \"string\",\n description = \"目标框架版本。支持:net8.0。默认为当前运行时版本。\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n },\n new\n {\n type = \"function\",\n function = new\n {\n name = \"compile_and_execute\",\n description = \"编译C#代码并立即执行,返回执行结果。适用于快速测试代码片段。\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n code = new\n {\n type = \"string\",\n description = \"要编译执行的C#源代码。必须包含Main方法。示例:'using System; class Program { static void Main() { Console.WriteLine(\\\"Hello\\\"); } }'\"\n },\n output_path = new\n {\n type = \"string\",\n description = \"编译输出的程序集文件路径。如果不指定,则仅在内存中编译和执行。示例:C:\\\\Temp\\\\MyProgram.dll\"\n },\n arguments = new\n {\n type = \"array\",\n items = new { type = \"string\" },\n description = \"传递给Main方法的命令行参数。示例:[\\\"arg1\\\", \\\"arg2\\\"]\"\n }\n },\n required = new[] { \"code\" }\n }\n }\n }\n };\n }\n\n\n public List<object> GetTaskCreationTools()\n {\n // 构建 TaskDefinition 的 JSON Schema 属性\n var taskDefinitionProperties = new Dictionary<string, object>\n {\n [\"task_id\"] = new\n {\n type = \"string\",\n description = \"任务ID,建议使用GUID\"\n },\n [\"project_name\"] = new\n {\n type = \"string\",\n description = \"工程文件名\"\n },\n [\"file_name\"] = new\n {\n type = \"string\",\n description = \"文件名\"\n },\n [\"file_path\"] = new\n {\n type = \"string\",\n description = \"文件路径\"\n },\n [\"function\"] = new\n {\n type = \"string\",\n description = \"文件功能描述\"\n },\n [\"dependencies\"] = new\n {\n type = \"array\",\n description = \"依赖的文件名列表\",\n items = new { type = \"string\" }\n },\n [\"requirements\"] = new\n {\n type = \"string\",\n description = \"创建该文件的具体要求\"\n },\n [\"estimated_complexity\"] = new\n {\n type = \"string\",\n description = \"估计复杂度\",\n @enum = new[] { \"low\", \"medium\", \"high\" }\n },\n [\"technology_requirements\"] = new\n {\n type = \"array\",\n description = \"技术需求列表\",\n items = new { type = \"string\" }\n },\n [\"testing_requirements\"] = new\n {\n type = \"string\",\n description = \"测试要求\"\n },\n [\"status\"] = new\n {\n type = \"string\",\n description = \"任务状态\",\n @default = \"pending\"\n },\n [\"assigned_to\"] = new\n {\n type = \"string\",\n description = \"分配给谁\",\n @default = \"CodingAgent\"\n }\n };\n\n // 任务定义所需的必填字段\n var requiredTaskProperties = new[] {\n \"task_id\",\"project_name\", \"file_name\", \"file_path\", \"function\", \"requirements\"\n };\n\n return new List<object>\n {\n new\n {\n type = \"function\",\n function = new\n {\n name = \"distribute_coding_tasks\",\n description = \"将文件创建和编码任务分配给CodingAgent,输入为任务列表\",\n parameters = new\n {\n type = \"object\",\n properties = new\n {\n tasks = new\n {\n type = \"array\",\n description = \"任务定义列表,符合TaskDefinition格式\",\n items = new\n {\n type = \"object\",\n properties = taskDefinitionProperties,\n required = requiredTaskProperties\n }\n },\n distribution_strategy = new\n {\n type = \"string\",\n description = \"分配策略\",\n @enum = new[] {\n \"sequential_by_dependency\",\n \"parallel_when_possible\",\n \"complexity_based\",\n \"priority_based\"\n },\n @default = \"sequential_by_dependency\"\n },\n execution_order = new\n {\n type = \"boolean\",\n description = \"是否返回任务执行顺序建议\",\n @default = true\n },\n batch_size = new\n {\n type = \"integer\",\n description = \"批次大小,一次分配多少个任务给CodingAgent\",\n @default = 1,\n minimum = 1,\n maximum = 10\n }\n },\n required = new[] { \"tasks\" }\n }\n }\n }\n };\n }\n public void Dispose()\n {\n _httpClient?.Dispose();\n }\n }\n\n public interface IToolApiService : IDisposable\n {\n Task<ToolCallResponse> SendToolRequestAsync(List<ChatMessage> conversationHistory, ToolApiType toolApiType);\n }\n\n // 工具调用响应模型\n public class ToolCallResponse\n {\n public string Content { get; set; }\n public List<ToolCall> ToolCalls { get; set; }\n public List<ToolCallInfo> ToolCallInfos { get; set; }\n public bool HasToolCalls { get; set; }\n public bool Success { get; set; }\n }\n\n public class ToolCallInfo\n {\n public string Id { get; set; }\n public string FunctionName { get; set; }\n public string Arguments { get; set; }\n }\n}"}}}}