Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions Docs/Build_and_Test_Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,8 @@ jobs:

#### 本地调试日志
```bash
# 启用详细日志
dotnet run --project TelegramSearchBot --verbosity detailed

# 使用 Serilog 配置
export SERILOG__MINIMUMLEVEL__DEFAULT=Debug
# Config.json 中默认 LogLevel=Verbose;需要降低噪声时可改为 Information/Warning
dotnet run --project TelegramSearchBot
```

#### 性能分析
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"OLTPAuth": "",
"OLTPAuthUrl": "",
"OLTPName": "",
"LogLevel": "Verbose",
"BraveApiKey": "",
"EnableAccounting": false
}
Expand Down Expand Up @@ -120,6 +121,7 @@
- `OLTPAuth`: OLTP日志推送认证密钥
- `OLTPAuthUrl`: OLTP日志推送URL
- `OLTPName`: OLTP日志推送名称
- `LogLevel`: Serilog 最小日志级别,支持 `Verbose`/`Debug`/`Information`/`Warning`/`Error`/`Fatal`,默认 `Verbose`

完整配置参考: [Env.cs](TelegramSearchBot.Common/Env.cs)

Expand Down
13 changes: 13 additions & 0 deletions TelegramSearchBot.Common/Env.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Serilog.Events;

namespace TelegramSearchBot.Common {
public static class Env {
Expand Down Expand Up @@ -42,6 +43,7 @@ static Env() {
OLTPAuth = config.OLTPAuth;
OLTPAuthUrl = config.OLTPAuthUrl;
OLTPName = config.OLTPName;
SerilogMinimumLevel = ResolveSerilogMinimumLevel(config.LogLevel);
BraveApiKey = config.BraveApiKey;
EnableAccounting = config.EnableAccounting;
EnableAutoUpdate = config.EnableAutoUpdate;
Expand Down Expand Up @@ -120,6 +122,7 @@ private static string NormalizeBaseUrl(string? baseUrl, string fallback) {
public static string OLTPAuth { get; set; } = null!;
public static string OLTPAuthUrl { get; set; } = null!;
public static string OLTPName { get; set; } = null!;
public static readonly LogEventLevel SerilogMinimumLevel;
public static string BraveApiKey { get; set; } = null!;
public static bool EnableAccounting { get; set; } = false;
public static int MaxToolCycles { get; set; }
Expand Down Expand Up @@ -153,6 +156,15 @@ public static string ResolveUpdateBaseUrl(Config config) {

return NormalizeBaseUrl(uri.ToString(), DefaultUpdateBaseUrl);
}

public static LogEventLevel ResolveSerilogMinimumLevel(string? logLevel) {
if (Enum.TryParse<LogEventLevel>(logLevel, ignoreCase: true, out var parsed) &&
Enum.IsDefined(typeof(LogEventLevel), parsed)) {
return parsed;
}

return LogEventLevel.Verbose;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
public class Config {
public string BaseUrl { get; set; } = "https://api.telegram.org";
Expand All @@ -178,6 +190,7 @@ public class Config {
public string OLTPAuth { get; set; } = null!;
public string OLTPAuthUrl { get; set; } = null!;
public string OLTPName { get; set; } = null!;
public string LogLevel { get; set; } = "Verbose";
public string BraveApiKey { get; set; } = null!;
public bool EnableAccounting { get; set; } = false;
public int MaxToolCycles { get; set; } = 25;
Expand Down
21 changes: 21 additions & 0 deletions TelegramSearchBot.Common/ExceptionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Reflection;

namespace TelegramSearchBot.Common {
public static class ExceptionExtensions {
public static Exception GetPrimaryException(this Exception exception) {
ArgumentNullException.ThrowIfNull(exception);

while ((exception is TargetInvocationException || exception is AggregateException) && exception.InnerException != null) {
exception = exception.InnerException;
}

return exception;
}

public static string GetLogSummary(this Exception exception) {
var primaryException = exception.GetPrimaryException();
return $"{primaryException.GetType().Name}: {primaryException.Message}";
}
}
}
62 changes: 52 additions & 10 deletions TelegramSearchBot.LLM/Service/AI/LLM/AnthropicService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,22 @@ private async IAsyncEnumerable<string> ExecWithNativeToolCallingAsync(
assistantContentBlocks.Add(new TextBlockParam(responseText));
}
foreach (var (id, name, inputJson) in toolUseBlocks) {
var parsedInput = string.IsNullOrWhiteSpace(inputJson)
? new Dictionary<string, JsonElement>()
: System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(inputJson);
Dictionary<string, JsonElement> parsedInput;
try {
parsedInput = string.IsNullOrWhiteSpace(inputJson)
? new Dictionary<string, JsonElement>()
: System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(inputJson);
} catch (Exception ex) {
_logger.LogError(
ex,
"{ServiceName}: Failed to deserialize Anthropic native tool input for assistant history. ToolUseId={ToolUseId}, ToolName={ToolName}, InputJson={InputJson}, ErrorSummary={ErrorSummary}",
ServiceName,
id,
name,
inputJson,
ex.GetLogSummary());
parsedInput = new Dictionary<string, JsonElement>();
}
assistantContentBlocks.Add(new ToolUseBlockParam {
ID = id,
Name = name,
Expand All @@ -615,7 +628,16 @@ private async IAsyncEnumerable<string> ExecWithNativeToolCallingAsync(
try {
argsDict = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(inputJson)
.ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
} catch { }
} catch (Exception ex) {
_logger.LogError(
ex,
"{ServiceName}: Failed to deserialize Anthropic native tool input for display. ToolUseId={ToolUseId}, ToolName={ToolName}, InputJson={InputJson}, ErrorSummary={ErrorSummary}",
ServiceName,
id,
name,
inputJson,
ex.GetLogSummary());
}
}
argsDict ??= new Dictionary<string, string>();
toolNamesBuilder.Append(McpToolHelper.FormatToolCallDisplay(name, argsDict));
Expand All @@ -642,8 +664,15 @@ private async IAsyncEnumerable<string> ExecWithNativeToolCallingAsync(
_logger.LogInformation("{ServiceName}: Tool {ToolName} executed. Result length: {Length}", ServiceName, name, toolResultString.Length);
} catch (Exception ex) {
isError = true;
_logger.LogError(ex, "{ServiceName}: Error executing tool {ToolName}.", ServiceName, name);
toolResultString = $"Error executing tool {name}: {ex.Message}";
_logger.LogError(
ex,
"{ServiceName}: Error executing Anthropic native tool {ToolName}. ToolUseId={ToolUseId}, InputJson={InputJson}, ErrorSummary={ErrorSummary}",
ServiceName,
name,
id,
inputJson,
ex.GetLogSummary());
toolResultString = $"Error executing tool {name}: {ex.GetLogSummary()}";
}

toolResultBlocks.Add(new ToolResultBlockParam(id) {
Expand Down Expand Up @@ -765,8 +794,14 @@ private async IAsyncEnumerable<string> ExecWithXmlToolCallingAsync(
_logger.LogInformation("{ServiceName}: Tool {ToolName} executed. Result: {Result}", ServiceName, parsedToolName, toolResultString);
} catch (Exception ex) {
isError = true;
_logger.LogError(ex, "{ServiceName}: Error executing tool {ToolName}.", ServiceName, parsedToolName);
toolResultString = $"Error executing tool {parsedToolName}: {ex.Message}.";
_logger.LogError(
ex,
"{ServiceName}: Error executing Anthropic XML tool {ToolName}. Arguments={Arguments}, ErrorSummary={ErrorSummary}",
ServiceName,
parsedToolName,
JsonConvert.SerializeObject(toolArguments),
ex.GetLogSummary());
toolResultString = $"Error executing tool {parsedToolName}: {ex.GetLogSummary()}.";
}

string feedbackPrefix = isError ? $"[Tool '{parsedToolName}' Execution Failed. Error: " : $"[Executed Tool '{parsedToolName}'. Result: ";
Expand Down Expand Up @@ -896,8 +931,15 @@ public async IAsyncEnumerable<string> ResumeFromSnapshotAsync(
toolResultString = McpToolHelper.ConvertToolResultToString(toolResultObject);
} catch (Exception ex) {
isError = true;
_logger.LogError(ex, "{ServiceName}: Error executing tool {ToolName} (resume).", ServiceName, parsedToolName);
toolResultString = $"Error executing tool {parsedToolName}: {ex.Message}.";
_logger.LogError(
ex,
"{ServiceName}: Error executing Anthropic XML tool {ToolName} (resume). Arguments={Arguments}, SnapshotId={SnapshotId}, ErrorSummary={ErrorSummary}",
ServiceName,
parsedToolName,
JsonConvert.SerializeObject(firstToolCall.arguments),
snapshot.SnapshotId,
ex.GetLogSummary());
toolResultString = $"Error executing tool {parsedToolName}: {ex.GetLogSummary()}.";
}

string feedbackPrefix = isError ? $"[Tool '{parsedToolName}' Execution Failed. Error: " : $"[Executed Tool '{parsedToolName}'. Result: ";
Expand Down
10 changes: 8 additions & 2 deletions TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,14 @@ public async IAsyncEnumerable<string> ExecAsync(
toolResult = McpToolHelper.ConvertToolResultToString(result);
} catch (Exception ex) {
isError = true;
_logger.LogError(ex, "Error executing tool {ToolName}", firstToolCall.toolName);
toolResult = $"Error executing tool {firstToolCall.toolName}: {ex.Message}";
_logger.LogError(
ex,
"{ServiceName}: Error executing Gemini XML tool {ToolName}. Arguments={Arguments}, ErrorSummary={ErrorSummary}",
ServiceName,
firstToolCall.toolName,
JsonConvert.SerializeObject(firstToolCall.arguments),
ex.GetLogSummary());
toolResult = $"Error executing tool {firstToolCall.toolName}: {ex.GetLogSummary()}";
}

string feedback = isError
Expand Down
Loading
Loading