Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/9486983_pet-yue.json
T

1 line
59 KiB
JSON

{"repo": "9486983/pet-yue", "files": {"plugins/DeepSeekPlugin/DeepSeekQueryPlugin.cs": {"0852398af638b85a21ded1daee0b1c89624c1cb1": {"t": 1785745823.9416232, "n": 7025, "h": "6a4cb11242c1fb86", "c": "using MyPersonalTool.Sdk;\nusing System.Text.Json;\n\nnamespace DeepSeekPlugin;\n\n/// <summary>\n/// DeepSeek 余额查询 &amp; 缓存测试插件\n/// 配置在设置页中管理(API Key、定时查询间隔)\n/// </summary>\n[Plugin(\"DeepSeek 查询\", Version = \"2.0.0\", Description = \"查询 DeepSeek API 余额、用量和缓存命中率,支持定时自动查询\")]\npublic class DeepSeekQueryPlugin : PluginBase\n{\n private const string KeyApiKey = \"deepseek_api_key\";\n private const string KeyInterval = \"deepseek_interval\";\n private const string KeyAutoQuery = \"deepseek_auto_query\";\n private const string KeySummaryUrl = \"deepseek_summary_url\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ChatUrl = \"https://api.deepseek.com/v1/chat/completions\";\n\n private IPluginHost? _host;\n private PeriodicTimer? _timer;\n private CancellationTokenSource? _timerCts;\n\n public override string Name => \"DeepSeek 查询\";\n public override string Description => \"查询 DeepSeek API 余额和缓存状态\";\n\n public override async Task InitializeAsync(IPluginHost host)\n {\n _host = host;\n await base.InitializeAsync(host);\n\n // ── 注册配置定义 ──\n host.RegisterConfig(new PluginConfigSection\n {\n Title = \"DeepSeek API\",\n Emoji = \"🔑\",\n Fields = new()\n {\n new()\n {\n Key = KeyApiKey,\n Label = \"API Key\",\n Type = PluginConfigFieldType.Password,\n IsRequired = true,\n Placeholder = \"sk-...\",\n Description = \"从 DeepSeek 控制台获取\",\n },\n new()\n {\n Key = KeyAutoQuery,\n Label = \"定时查询余额\",\n Type = PluginConfigFieldType.Boolean,\n DefaultValue = \"false\",\n Description = \"开启后将按下方间隔自动查询余额并显示气泡\",\n },\n new()\n {\n Key = KeyInterval,\n Label = \"查询间隔(分钟)\",\n Type = PluginConfigFieldType.Number,\n DefaultValue = \"30\",\n MinValue = 1,\n MaxValue = 180,\n Description = \"两次自动查询之间的间隔时间\",\n },\n new()\n {\n Key = KeySummaryUrl,\n Label = \"用量查询接口\",\n Type = PluginConfigFieldType.String,\n DefaultValue = \"https://platform.deepseek.com/api/v0/users/get_user_summary\",\n Description = \"待后续接入,当前暂未使用\",\n },\n },\n }, Name);\n\n // ── 监听配置变更 ──\n host.ConfigValueChanged += OnConfigChanged;\n\n // ── 注册动作 ──\n\n host.RegisterAction(new PluginAction\n {\n Name = \"设置\",\n Emoji = \"⚙️\",\n Description = \"配置 DeepSeek API Key 和定时查询参数\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = () =>\n {\n host.ShowConfigDialog(\"DeepSeek API\");\n return Task.CompletedTask;\n },\n });\n\n host.RegisterAction(new PluginAction\n {\n Name = \"查询余额\",\n Emoji = \"💰\",\n Description = \"查询 DeepSeek API 余额\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await QueryBalance(),\n });\n\n // ── 启动定时查询(如果已启用) ──\n RestartTimerIfNeeded();\n\n host.Log(\"DeepSeek 查询插件 v2.0 已加载\");\n }\n\n private string? GetApiKey() => _host?.GetConfig(KeyApiKey);\n private bool IsAutoQueryEnabled => _host?.GetConfig(KeyAutoQuery) == \"true\";\n private int GetQueryIntervalMinutes()\n {\n var val = _host?.GetConfig(KeyInterval);\n return int.TryParse(val, out var m) && m >= 1 ? m : 30;\n }\n\n // ── 定时查询 ──\n\n private void RestartTimerIfNeeded()\n {\n StopTimer();\n\n if (!IsAutoQueryEnabled) return;\n if (_host == null) return;\n\n var interval = GetQueryIntervalMinutes();\n _timerCts = new CancellationTokenSource();\n var ct = _timerCts.Token;\n _timer = new PeriodicTimer(TimeSpan.FromMinutes(interval));\n\n _ = Task.Run(async () =>\n {\n try\n {\n while (await _timer.WaitForNextTickAsync(ct))\n {\n if (string.IsNullOrEmpty(GetApiKey())) continue;\n await QueryBalance(silent: true);\n if (!IsAutoQueryEnabled) break;\n }\n }\n catch (OperationCanceledException) { }\n }, ct);\n }\n\n private void StopTimer()\n {\n _timerCts?.Cancel();\n _timerCts?.Dispose();\n _timerCts = null;\n _timer?.Dispose();\n _timer = null;\n }\n\n private void OnConfigChanged(object? sender, string key)\n {\n if (key is KeyAutoQuery or KeyInterval)\n RestartTimerIfNeeded();\n }\n\n // ── 查询余额 ──\n\n private async Task QueryBalance(bool silent = false)\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n if (!silent)\n _host.ShowThought(\"⚠️ 未配置\", \"请在设置 → DeepSeek API 中填写 API Key\");\n return;\n }\n\n try\n {\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n\n var json = await client.GetStringAsync(BalanceUrl);\n using var doc = JsonDocument.Parse(json);\n\n var balance = \"\";\n var isAvailable = false;\n if (doc.RootElement.TryGetProperty(\"balance_infos\", out var infos) &&\n infos.ValueKind == JsonValueKind.Array && infos.GetArrayLength() > 0)\n {\n balance = infos[0].TryGetProperty(\"total_balance\", out var b)\n ? b.GetString() ?? \"0\" : \"0\";\n }\n if (doc.RootElement.TryGetProperty(\"is_available\", out var avail))\n isAvailable = avail.GetBoolean();\n\n if (string.IsNullOrEmpty(balance) || balance == \"0\")\n {\n if (!silent)\n _host.ShowThought(\"💰 余额\", \"暂未获取到余额信息\");\n return;\n }\n\n var status = isAvailable ? \"✅ 可用\" : \"❌ 不可用\";\n var title = \"💰 DeepSeek 余额\";\n _host.ShowThought(title, $\"{status}\\n💰 余额: ¥{balance}\");\n _host.ShowReaction(\"💰\");\n }\n catch (HttpRequestException ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\", ex.Message);\n }\n }\n\n public override Task CleanupAsync()\n {\n StopTimer();\n return base.CleanupAsync();\n }\n}\n"}, "95a3874842683dce386ceaf888db82007fbafd91": {"t": 1785756818.214374, "n": 7025, "h": "6a4cb11242c1fb86", "c": "using MyPersonalTool.Sdk;\nusing System.Text.Json;\n\nnamespace DeepSeekPlugin;\n\n/// <summary>\n/// DeepSeek 余额查询 &amp; 缓存测试插件\n/// 配置在设置页中管理(API Key、定时查询间隔)\n/// </summary>\n[Plugin(\"DeepSeek 查询\", Version = \"2.0.0\", Description = \"查询 DeepSeek API 余额、用量和缓存命中率,支持定时自动查询\")]\npublic class DeepSeekQueryPlugin : PluginBase\n{\n private const string KeyApiKey = \"deepseek_api_key\";\n private const string KeyInterval = \"deepseek_interval\";\n private const string KeyAutoQuery = \"deepseek_auto_query\";\n private const string KeySummaryUrl = \"deepseek_summary_url\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ChatUrl = \"https://api.deepseek.com/v1/chat/completions\";\n\n private IPluginHost? _host;\n private PeriodicTimer? _timer;\n private CancellationTokenSource? _timerCts;\n\n public override string Name => \"DeepSeek 查询\";\n public override string Description => \"查询 DeepSeek API 余额和缓存状态\";\n\n public override async Task InitializeAsync(IPluginHost host)\n {\n _host = host;\n await base.InitializeAsync(host);\n\n // ── 注册配置定义 ──\n host.RegisterConfig(new PluginConfigSection\n {\n Title = \"DeepSeek API\",\n Emoji = \"🔑\",\n Fields = new()\n {\n new()\n {\n Key = KeyApiKey,\n Label = \"API Key\",\n Type = PluginConfigFieldType.Password,\n IsRequired = true,\n Placeholder = \"sk-...\",\n Description = \"从 DeepSeek 控制台获取\",\n },\n new()\n {\n Key = KeyAutoQuery,\n Label = \"定时查询余额\",\n Type = PluginConfigFieldType.Boolean,\n DefaultValue = \"false\",\n Description = \"开启后将按下方间隔自动查询余额并显示气泡\",\n },\n new()\n {\n Key = KeyInterval,\n Label = \"查询间隔(分钟)\",\n Type = PluginConfigFieldType.Number,\n DefaultValue = \"30\",\n MinValue = 1,\n MaxValue = 180,\n Description = \"两次自动查询之间的间隔时间\",\n },\n new()\n {\n Key = KeySummaryUrl,\n Label = \"用量查询接口\",\n Type = PluginConfigFieldType.String,\n DefaultValue = \"https://platform.deepseek.com/api/v0/users/get_user_summary\",\n Description = \"待后续接入,当前暂未使用\",\n },\n },\n }, Name);\n\n // ── 监听配置变更 ──\n host.ConfigValueChanged += OnConfigChanged;\n\n // ── 注册动作 ──\n\n host.RegisterAction(new PluginAction\n {\n Name = \"设置\",\n Emoji = \"⚙️\",\n Description = \"配置 DeepSeek API Key 和定时查询参数\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = () =>\n {\n host.ShowConfigDialog(\"DeepSeek API\");\n return Task.CompletedTask;\n },\n });\n\n host.RegisterAction(new PluginAction\n {\n Name = \"查询余额\",\n Emoji = \"💰\",\n Description = \"查询 DeepSeek API 余额\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await QueryBalance(),\n });\n\n // ── 启动定时查询(如果已启用) ──\n RestartTimerIfNeeded();\n\n host.Log(\"DeepSeek 查询插件 v2.0 已加载\");\n }\n\n private string? GetApiKey() => _host?.GetConfig(KeyApiKey);\n private bool IsAutoQueryEnabled => _host?.GetConfig(KeyAutoQuery) == \"true\";\n private int GetQueryIntervalMinutes()\n {\n var val = _host?.GetConfig(KeyInterval);\n return int.TryParse(val, out var m) && m >= 1 ? m : 30;\n }\n\n // ── 定时查询 ──\n\n private void RestartTimerIfNeeded()\n {\n StopTimer();\n\n if (!IsAutoQueryEnabled) return;\n if (_host == null) return;\n\n var interval = GetQueryIntervalMinutes();\n _timerCts = new CancellationTokenSource();\n var ct = _timerCts.Token;\n _timer = new PeriodicTimer(TimeSpan.FromMinutes(interval));\n\n _ = Task.Run(async () =>\n {\n try\n {\n while (await _timer.WaitForNextTickAsync(ct))\n {\n if (string.IsNullOrEmpty(GetApiKey())) continue;\n await QueryBalance(silent: true);\n if (!IsAutoQueryEnabled) break;\n }\n }\n catch (OperationCanceledException) { }\n }, ct);\n }\n\n private void StopTimer()\n {\n _timerCts?.Cancel();\n _timerCts?.Dispose();\n _timerCts = null;\n _timer?.Dispose();\n _timer = null;\n }\n\n private void OnConfigChanged(object? sender, string key)\n {\n if (key is KeyAutoQuery or KeyInterval)\n RestartTimerIfNeeded();\n }\n\n // ── 查询余额 ──\n\n private async Task QueryBalance(bool silent = false)\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n if (!silent)\n _host.ShowThought(\"⚠️ 未配置\", \"请在设置 → DeepSeek API 中填写 API Key\");\n return;\n }\n\n try\n {\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n\n var json = await client.GetStringAsync(BalanceUrl);\n using var doc = JsonDocument.Parse(json);\n\n var balance = \"\";\n var isAvailable = false;\n if (doc.RootElement.TryGetProperty(\"balance_infos\", out var infos) &&\n infos.ValueKind == JsonValueKind.Array && infos.GetArrayLength() > 0)\n {\n balance = infos[0].TryGetProperty(\"total_balance\", out var b)\n ? b.GetString() ?? \"0\" : \"0\";\n }\n if (doc.RootElement.TryGetProperty(\"is_available\", out var avail))\n isAvailable = avail.GetBoolean();\n\n if (string.IsNullOrEmpty(balance) || balance == \"0\")\n {\n if (!silent)\n _host.ShowThought(\"💰 余额\", \"暂未获取到余额信息\");\n return;\n }\n\n var status = isAvailable ? \"✅ 可用\" : \"❌ 不可用\";\n var title = \"💰 DeepSeek 余额\";\n _host.ShowThought(title, $\"{status}\\n💰 余额: ¥{balance}\");\n _host.ShowReaction(\"💰\");\n }\n catch (HttpRequestException ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\", ex.Message);\n }\n }\n\n public override Task CleanupAsync()\n {\n StopTimer();\n return base.CleanupAsync();\n }\n}\n"}, "3e08bdccb8462b5d62ecdc19dd486da6f7a1dabe": {"t": 1785756819.511552, "n": 10780, "h": "0d554f306ac6a163", "c": "using System.Text;\nusing System.Text.Json;\nusing MyPersonalTool.Sdk;\n\nnamespace DeepSeekPlugin;\n\n/// <summary>\n/// DeepSeek 余额查询 &amp; 缓存测试插件\n/// 配置在设置页中管理(API Key、定时查询间隔)\n/// </summary>\n[Plugin(\"DeepSeek 查询\", Version = \"2.0.0\", Description = \"查询 DeepSeek API 余额、用量和缓存命中率,支持定时自动查询\")]\npublic class DeepSeekQueryPlugin : PluginBase\n{\n private const string KeyApiKey = \"deepseek_api_key\";\n private const string KeyInterval = \"deepseek_interval\";\n private const string KeyAutoQuery = \"deepseek_auto_query\";\n private const string KeySummaryUrl = \"deepseek_summary_url\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ChatUrl = \"https://api.deepseek.com/v1/chat/completions\";\n\n private IPluginHost? _host;\n private PeriodicTimer? _timer;\n private CancellationTokenSource? _timerCts;\n\n public override string Name => \"DeepSeek 查询\";\n public override string Description => \"查询 DeepSeek API 余额和缓存状态\";\n\n public override async Task InitializeAsync(IPluginHost host)\n {\n _host = host;\n await base.InitializeAsync(host);\n\n // ── 注册配置定义 ──\n host.RegisterConfig(new PluginConfigSection\n {\n Title = \"DeepSeek API\",\n Emoji = \"🔑\",\n Fields = new()\n {\n new()\n {\n Key = KeyApiKey,\n Label = \"API Key\",\n Type = PluginConfigFieldType.Password,\n IsRequired = true,\n Placeholder = \"sk-...\",\n Description = \"从 DeepSeek 控制台获取\",\n },\n new()\n {\n Key = KeyAutoQuery,\n Label = \"定时查询余额\",\n Type = PluginConfigFieldType.Boolean,\n DefaultValue = \"false\",\n Description = \"开启后将按下方间隔自动查询余额并显示气泡\",\n },\n new()\n {\n Key = KeyInterval,\n Label = \"查询间隔(分钟)\",\n Type = PluginConfigFieldType.Number,\n DefaultValue = \"30\",\n MinValue = 1,\n MaxValue = 180,\n Description = \"两次自动查询之间的间隔时间\",\n },\n new()\n {\n Key = KeySummaryUrl,\n Label = \"用量查询接口\",\n Type = PluginConfigFieldType.String,\n DefaultValue = \"https://platform.deepseek.com/api/v0/users/get_user_summary\",\n Description = \"待后续接入,当前暂未使用\",\n },\n },\n }, Name);\n\n // ── 监听配置变更 ──\n host.ConfigValueChanged += OnConfigChanged;\n\n // ── 注册动作 ──\n\n host.RegisterAction(new PluginAction\n {\n Name = \"设置\",\n Emoji = \"⚙️\",\n Description = \"配置 DeepSeek API Key 和定时查询参数\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = () =>\n {\n host.ShowConfigDialog(\"DeepSeek API\");\n return Task.CompletedTask;\n },\n });\n\n host.RegisterAction(new PluginAction\n {\n Name = \"查询余额\",\n Emoji = \"💰\",\n Description = \"查询 DeepSeek API 余额\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await QueryBalance(),\n });\n\n host.RegisterAction(new PluginAction\n {\n Name = \"缓存命中测试\",\n Emoji = \"⚡\",\n Description = \"发送两次请求对比缓存命中率\",\n Group = \"🔑 DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await TestCache(),\n });\n\n // ── 启动定时查询(如果已启用) ──\n RestartTimerIfNeeded();\n\n host.Log(\"DeepSeek 查询插件 v2.0 已加载\");\n }\n\n private string? GetApiKey() => _host?.GetConfig(KeyApiKey);\n private bool IsAutoQueryEnabled => _host?.GetConfig(KeyAutoQuery) == \"true\";\n private int GetQueryIntervalMinutes()\n {\n var val = _host?.GetConfig(KeyInterval);\n return int.TryParse(val, out var m) && m >= 1 ? m : 30;\n }\n\n // ── 定时查询 ──\n\n private void RestartTimerIfNeeded()\n {\n StopTimer();\n\n if (!IsAutoQueryEnabled) return;\n if (_host == null) return;\n\n var interval = GetQueryIntervalMinutes();\n _timerCts = new CancellationTokenSource();\n var ct = _timerCts.Token;\n _timer = new PeriodicTimer(TimeSpan.FromMinutes(interval));\n\n _ = Task.Run(async () =>\n {\n try\n {\n while (await _timer.WaitForNextTickAsync(ct))\n {\n if (string.IsNullOrEmpty(GetApiKey())) continue;\n await QueryBalance(silent: true);\n if (!IsAutoQueryEnabled) break;\n }\n }\n catch (OperationCanceledException) { }\n }, ct);\n }\n\n private void StopTimer()\n {\n _timerCts?.Cancel();\n _timerCts?.Dispose();\n _timerCts = null;\n _timer?.Dispose();\n _timer = null;\n }\n\n private void OnConfigChanged(object? sender, string key)\n {\n if (key is KeyAutoQuery or KeyInterval)\n RestartTimerIfNeeded();\n }\n\n // ── 查询余额 ──\n\n private async Task QueryBalance(bool silent = false)\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n if (!silent)\n _host.ShowThought(\"⚠️ 未配置\", \"请在设置 → DeepSeek API 中填写 API Key\");\n return;\n }\n\n try\n {\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n\n var json = await client.GetStringAsync(BalanceUrl);\n using var doc = JsonDocument.Parse(json);\n\n var balance = \"\";\n var isAvailable = false;\n if (doc.RootElement.TryGetProperty(\"balance_infos\", out var infos) &&\n infos.ValueKind == JsonValueKind.Array && infos.GetArrayLength() > 0)\n {\n balance = infos[0].TryGetProperty(\"total_balance\", out var b)\n ? b.GetString() ?? \"0\" : \"0\";\n }\n if (doc.RootElement.TryGetProperty(\"is_available\", out var avail))\n isAvailable = avail.GetBoolean();\n\n if (string.IsNullOrEmpty(balance) || balance == \"0\")\n {\n if (!silent)\n _host.ShowThought(\"💰 余额\", \"暂未获取到余额信息\");\n return;\n }\n\n var status = isAvailable ? \"✅ 可用\" : \"❌ 不可用\";\n var title = silent ? $\"💰 ¥{balance}\" : \"💰 DeepSeek 余额\";\n _host.ShowThought(title, $\"{status}\\n💰 余额: ¥{balance}\");\n _host.ShowReaction(silent ? \"💹\" : \"💰\");\n }\n catch (HttpRequestException ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\", ex.Message);\n }\n }\n\n // ── 缓存测试 ──\n\n private async Task TestCache()\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n _host.ShowThought(\"⚠️ 未配置\", \"请在设置页 → DeepSeek API 中填写 API Key\");\n return;\n }\n\n try\n {\n _host.ShowThought(\"⚡ 缓存测试\", \"正在发送第 1 次请求…\");\n\n var body = JsonSerializer.Serialize(new\n {\n model = \"deepseek-chat\",\n messages = new[] { new { role = \"user\", content = \"Hello\" } },\n max_tokens = 1\n });\n\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n var content = new StringContent(body, Encoding.UTF8, \"application/json\");\n\n var resp1 = await client.PostAsync(ChatUrl, content);\n var json1 = await resp1.Content.ReadAsStringAsync();\n var usage1 = ParseUsage(json1);\n\n _host.ShowThought(\"⚡ 缓存测试\", \"第 1 次请求完成,正在发送第 2 次…\");\n\n var resp2 = await client.PostAsync(ChatUrl, content);\n var json2 = await resp2.Content.ReadAsStringAsync();\n var usage2 = ParseUsage(json2);\n\n var hitRate1 = usage1.TotalInput > 0\n ? (double)usage1.CacheHit / usage1.TotalInput * 100 : 0;\n var hitRate2 = usage2.TotalInput > 0\n ? (double)usage2.CacheHit / usage2.TotalInput * 100 : 0;\n\n var msg = $\"【第 1 次请求】\\n\" +\n $\" 输入 tokens: {usage1.TotalInput}\\n\" +\n $\" 缓存命中: {usage1.CacheHit}\\n\" +\n $\" 缓存未命中: {usage1.CacheMiss}\\n\" +\n $\" 命中率: {hitRate1:F1}%\\n\\n\" +\n $\"【第 2 次请求(相同前缀)】\\n\" +\n $\" 输入 tokens: {usage2.TotalInput}\\n\" +\n $\" 缓存命中: {usage2.CacheHit}\\n\" +\n $\" 缓存未命中: {usage2.CacheMiss}\\n\" +\n $\" 命中率: {hitRate2:F1}%\\n\\n\" +\n $\"💡 第 2 次命中率应明显高于第 1 次\\n\" +\n $\"(前提是两次请求在缓存 TTL 内)\";\n\n _host.ShowThought(\"⚡ DeepSeek 缓存测试\", msg);\n _host.ShowReaction(\"⚡\");\n }\n catch (HttpRequestException ex)\n {\n _host.ShowThought(\"❌ 测试失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n _host.ShowThought(\"❌ 测试失败\", ex.Message);\n }\n }\n\n private static UsageInfo ParseUsage(string json)\n {\n try\n {\n using var doc = JsonDocument.Parse(json);\n var usage = doc.RootElement.GetProperty(\"usage\");\n var hit = usage.TryGetProperty(\"prompt_cache_hit_tokens\", out var h) ? h.GetInt64() : 0;\n var miss = usage.TryGetProperty(\"prompt_cache_miss_tokens\", out var m) ? m.GetInt64() : 0;\n var input = usage.TryGetProperty(\"prompt_tokens\", out var p) ? p.GetInt64() : 0;\n var output = usage.TryGetProperty(\"completion_tokens\", out var c) ? c.GetInt64() : 0;\n return new UsageInfo(input, output, hit, miss);\n }\n catch\n {\n return new UsageInfo(0, 0, 0, 0);\n }\n }\n\n public override Task CleanupAsync()\n {\n StopTimer();\n return base.CleanupAsync();\n }\n\n private record UsageInfo(long Input, long Output, long CacheHit, long CacheMiss)\n {\n public long TotalInput => CacheHit + CacheMiss;\n }\n}\n"}, "d3ec70b001a2be899c7612f3f934d7675f223a7a": {"t": 1785756820.987575, "n": 10774, "h": "e9aca1584867379a", "c": "using System.Text;\nusing System.Text.Json;\nusing MyPersonalTool.Sdk;\n\nnamespace DeepSeekPlugin;\n\n/// <summary>\n/// DeepSeek 余额查询 &amp; 缓存测试插件\n/// 配置在设置页中管理(API Key、定时查询间隔)\n/// </summary>\n[Plugin(\"DeepSeek 查询\", Version = \"2.0.0\", Description = \"查询 DeepSeek API 余额、用量和缓存命中率,支持定时自动查询\")]\npublic class DeepSeekQueryPlugin : PluginBase\n{\n private const string KeyApiKey = \"deepseek_api_key\";\n private const string KeyInterval = \"deepseek_interval\";\n private const string KeyAutoQuery = \"deepseek_auto_query\";\n private const string KeySummaryUrl = \"deepseek_summary_url\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ChatUrl = \"https://api.deepseek.com/v1/chat/completions\";\n\n private IPluginHost? _host;\n private PeriodicTimer? _timer;\n private CancellationTokenSource? _timerCts;\n\n public override string Name => \"DeepSeek 查询\";\n public override string Description => \"查询 DeepSeek API 余额和缓存状态\";\n\n public override async Task InitializeAsync(IPluginHost host)\n {\n _host = host;\n await base.InitializeAsync(host);\n\n // ── 注册配置定义 ──\n host.RegisterConfig(new PluginConfigSection\n {\n Title = \"DeepSeek API\",\n Emoji = \"🔑\",\n Fields = new()\n {\n new()\n {\n Key = KeyApiKey,\n Label = \"API Key\",\n Type = PluginConfigFieldType.Password,\n IsRequired = true,\n Placeholder = \"sk-...\",\n Description = \"从 DeepSeek 控制台获取\",\n },\n new()\n {\n Key = KeyAutoQuery,\n Label = \"定时查询余额\",\n Type = PluginConfigFieldType.Boolean,\n DefaultValue = \"false\",\n Description = \"开启后将按下方间隔自动查询余额并显示气泡\",\n },\n new()\n {\n Key = KeyInterval,\n Label = \"查询间隔(分钟)\",\n Type = PluginConfigFieldType.Number,\n DefaultValue = \"30\",\n MinValue = 1,\n MaxValue = 180,\n Description = \"两次自动查询之间的间隔时间\",\n },\n new()\n {\n Key = KeySummaryUrl,\n Label = \"用量查询接口\",\n Type = PluginConfigFieldType.String,\n DefaultValue = \"https://platform.deepseek.com/api/v0/users/get_user_summary\",\n Description = \"待后续接入,当前暂未使用\",\n },\n },\n }, Name);\n\n // ── 监听配置变更 ──\n host.ConfigValueChanged += OnConfigChanged;\n\n // ── 注册动作 ──\n\n host.RegisterAction(new PluginAction\n {\n Name = \"设置\",\n Emoji = \"⚙️\",\n Description = \"配置 DeepSeek API Key 和定时查询参数\",\n Group = \"DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = () =>\n {\n host.ShowConfigDialog(\"DeepSeek API\");\n return Task.CompletedTask;\n },\n });\n\n host.RegisterAction(new PluginAction\n {\n Name = \"查询余额\",\n Emoji = \"💰\",\n Description = \"查询 DeepSeek API 余额\",\n Group = \"DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await QueryBalance(),\n });\n\n host.RegisterAction(new PluginAction\n {\n Name = \"缓存命中测试\",\n Emoji = \"⚡\",\n Description = \"发送两次请求对比缓存命中率\",\n Group = \"DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await TestCache(),\n });\n\n // ── 启动定时查询(如果已启用) ──\n RestartTimerIfNeeded();\n\n host.Log(\"DeepSeek 查询插件 v2.0 已加载\");\n }\n\n private string? GetApiKey() => _host?.GetConfig(KeyApiKey);\n private bool IsAutoQueryEnabled => _host?.GetConfig(KeyAutoQuery) == \"true\";\n private int GetQueryIntervalMinutes()\n {\n var val = _host?.GetConfig(KeyInterval);\n return int.TryParse(val, out var m) && m >= 1 ? m : 30;\n }\n\n // ── 定时查询 ──\n\n private void RestartTimerIfNeeded()\n {\n StopTimer();\n\n if (!IsAutoQueryEnabled) return;\n if (_host == null) return;\n\n var interval = GetQueryIntervalMinutes();\n _timerCts = new CancellationTokenSource();\n var ct = _timerCts.Token;\n _timer = new PeriodicTimer(TimeSpan.FromMinutes(interval));\n\n _ = Task.Run(async () =>\n {\n try\n {\n while (await _timer.WaitForNextTickAsync(ct))\n {\n if (string.IsNullOrEmpty(GetApiKey())) continue;\n await QueryBalance(silent: true);\n if (!IsAutoQueryEnabled) break;\n }\n }\n catch (OperationCanceledException) { }\n }, ct);\n }\n\n private void StopTimer()\n {\n _timerCts?.Cancel();\n _timerCts?.Dispose();\n _timerCts = null;\n _timer?.Dispose();\n _timer = null;\n }\n\n private void OnConfigChanged(object? sender, string key)\n {\n if (key is KeyAutoQuery or KeyInterval)\n RestartTimerIfNeeded();\n }\n\n // ── 查询余额 ──\n\n private async Task QueryBalance(bool silent = false)\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n if (!silent)\n _host.ShowThought(\"⚠️ 未配置\", \"请在设置 → DeepSeek API 中填写 API Key\");\n return;\n }\n\n try\n {\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n\n var json = await client.GetStringAsync(BalanceUrl);\n using var doc = JsonDocument.Parse(json);\n\n var balance = \"\";\n var isAvailable = false;\n if (doc.RootElement.TryGetProperty(\"balance_infos\", out var infos) &&\n infos.ValueKind == JsonValueKind.Array && infos.GetArrayLength() > 0)\n {\n balance = infos[0].TryGetProperty(\"total_balance\", out var b)\n ? b.GetString() ?? \"0\" : \"0\";\n }\n if (doc.RootElement.TryGetProperty(\"is_available\", out var avail))\n isAvailable = avail.GetBoolean();\n\n if (string.IsNullOrEmpty(balance) || balance == \"0\")\n {\n if (!silent)\n _host.ShowThought(\"💰 余额\", \"暂未获取到余额信息\");\n return;\n }\n\n var status = isAvailable ? \"✅ 可用\" : \"❌ 不可用\";\n var title = silent ? $\"💰 ¥{balance}\" : \"💰 DeepSeek 余额\";\n _host.ShowThought(title, $\"{status}\\n💰 余额: ¥{balance}\");\n _host.ShowReaction(silent ? \"💹\" : \"💰\");\n }\n catch (HttpRequestException ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n if (!silent)\n _host.ShowThought(\"❌ 查询失败\", ex.Message);\n }\n }\n\n // ── 缓存测试 ──\n\n private async Task TestCache()\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n _host.ShowThought(\"⚠️ 未配置\", \"请在设置页 → DeepSeek API 中填写 API Key\");\n return;\n }\n\n try\n {\n _host.ShowThought(\"⚡ 缓存测试\", \"正在发送第 1 次请求…\");\n\n var body = JsonSerializer.Serialize(new\n {\n model = \"deepseek-chat\",\n messages = new[] { new { role = \"user\", content = \"Hello\" } },\n max_tokens = 1\n });\n\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n var content = new StringContent(body, Encoding.UTF8, \"application/json\");\n\n var resp1 = await client.PostAsync(ChatUrl, content);\n var json1 = await resp1.Content.ReadAsStringAsync();\n var usage1 = ParseUsage(json1);\n\n _host.ShowThought(\"⚡ 缓存测试\", \"第 1 次请求完成,正在发送第 2 次…\");\n\n var resp2 = await client.PostAsync(ChatUrl, content);\n var json2 = await resp2.Content.ReadAsStringAsync();\n var usage2 = ParseUsage(json2);\n\n var hitRate1 = usage1.TotalInput > 0\n ? (double)usage1.CacheHit / usage1.TotalInput * 100 : 0;\n var hitRate2 = usage2.TotalInput > 0\n ? (double)usage2.CacheHit / usage2.TotalInput * 100 : 0;\n\n var msg = $\"【第 1 次请求】\\n\" +\n $\" 输入 tokens: {usage1.TotalInput}\\n\" +\n $\" 缓存命中: {usage1.CacheHit}\\n\" +\n $\" 缓存未命中: {usage1.CacheMiss}\\n\" +\n $\" 命中率: {hitRate1:F1}%\\n\\n\" +\n $\"【第 2 次请求(相同前缀)】\\n\" +\n $\" 输入 tokens: {usage2.TotalInput}\\n\" +\n $\" 缓存命中: {usage2.CacheHit}\\n\" +\n $\" 缓存未命中: {usage2.CacheMiss}\\n\" +\n $\" 命中率: {hitRate2:F1}%\\n\\n\" +\n $\"💡 第 2 次命中率应明显高于第 1 次\\n\" +\n $\"(前提是两次请求在缓存 TTL 内)\";\n\n _host.ShowThought(\"⚡ DeepSeek 缓存测试\", msg);\n _host.ShowReaction(\"⚡\");\n }\n catch (HttpRequestException ex)\n {\n _host.ShowThought(\"❌ 测试失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n _host.ShowThought(\"❌ 测试失败\", ex.Message);\n }\n }\n\n private static UsageInfo ParseUsage(string json)\n {\n try\n {\n using var doc = JsonDocument.Parse(json);\n var usage = doc.RootElement.GetProperty(\"usage\");\n var hit = usage.TryGetProperty(\"prompt_cache_hit_tokens\", out var h) ? h.GetInt64() : 0;\n var miss = usage.TryGetProperty(\"prompt_cache_miss_tokens\", out var m) ? m.GetInt64() : 0;\n var input = usage.TryGetProperty(\"prompt_tokens\", out var p) ? p.GetInt64() : 0;\n var output = usage.TryGetProperty(\"completion_tokens\", out var c) ? c.GetInt64() : 0;\n return new UsageInfo(input, output, hit, miss);\n }\n catch\n {\n return new UsageInfo(0, 0, 0, 0);\n }\n }\n\n public override Task CleanupAsync()\n {\n StopTimer();\n return base.CleanupAsync();\n }\n\n private record UsageInfo(long Input, long Output, long CacheHit, long CacheMiss)\n {\n public long TotalInput => CacheHit + CacheMiss;\n }\n}\n"}, "fd1ae73b56ed5507bedc7b6e1170d86e491202a2": {"t": 1785756822.5190926, "n": 8417, "h": "b929137a3de91eed", "c": "using System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing MyPersonalTool.Sdk;\n\nnamespace DeepSeekPlugin;\n\n/// <summary>\n/// DeepSeek 余额查询 &amp; 缓存测试插件\n/// 首次使用需通过右键菜单设置 API Key\n/// </summary>\n[Plugin(\"DeepSeek 查询\", Version = \"1.1.0\", Description = \"查询 DeepSeek API 余额、用量和缓存命中率\")]\npublic class DeepSeekQueryPlugin : PluginBase\n{\n private const string ConfigKey = \"deepseek_api_key\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ChatUrl = \"https://api.deepseek.com/v1/chat/completions\";\n\n private IPluginHost? _host;\n\n public override string Name => \"DeepSeek 查询\";\n public override string Description => \"查询 DeepSeek API 余额和缓存状态\";\n\n public override async Task InitializeAsync(IPluginHost host)\n {\n _host = host;\n await base.InitializeAsync(host);\n\n // 设置 API Key\n host.RegisterAction(new PluginAction\n {\n Name = \"设置 API Key\",\n Emoji = \"🔑\",\n Description = \"配置 DeepSeek API Key\",\n Group = \"DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () =>\n {\n var currentKey = host.GetConfig(ConfigKey) ?? \"\";\n var key = await host.ShowInputDialog(\n \"🔑 DeepSeek API Key\",\n \"输入你的 DeepSeek API Key (sk-...)\",\n currentKey);\n if (string.IsNullOrEmpty(key)) return;\n\n host.SetConfig(ConfigKey, key);\n host.ShowThought(\"✅ 配置已保存\",\n \"DeepSeek API Key 已保存,现在可以使用查询功能。\");\n },\n });\n\n // 查询余额\n host.RegisterAction(new PluginAction\n {\n Name = \"查询余额\",\n Emoji = \"💰\",\n Description = \"查询 DeepSeek API 余额和账户状态\",\n Group = \"DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await QueryBalance(),\n });\n\n // 缓存测试\n host.RegisterAction(new PluginAction\n {\n Name = \"缓存命中测试\",\n Emoji = \"⚡\",\n Description = \"发送两次请求对比缓存命中率\",\n Group = \"DeepSeek\",\n Target = ActionTarget.ContextMenu,\n Callback = async () => await TestCache(),\n });\n\n host.Log(\"DeepSeek 查询插件已加载\");\n }\n\n private string? GetApiKey() => _host?.GetConfig(ConfigKey);\n\n private async Task QueryBalance()\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n _host.ShowThought(\"⚠️ 未配置\", \"请先右键 → DeepSeek → 设置 API Key\");\n return;\n }\n\n try\n {\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n\n var json = await client.GetStringAsync(BalanceUrl);\n var resp = JsonSerializer.Deserialize<BalanceResponse>(json);\n\n if (resp?.BalanceInfos == null || resp.BalanceInfos.Count == 0)\n {\n _host.ShowThought(\"💰 余额\", \"暂未获取到余额信息\");\n return;\n }\n\n var info = resp.BalanceInfos[0];\n var available = resp.IsAvailable ? \"✅ 可用\" : \"❌ 不可用\";\n var msg = $\"状态: {available}\\n\" +\n $\"余额: ¥{info.TotalBalance}\\n\" +\n (string.IsNullOrEmpty(info.ToppedUpBalance) || info.ToppedUpBalance == \"0.00\"\n ? \"\"\n : $\"充值余额: ¥{info.ToppedUpBalance}\\n\") +\n (string.IsNullOrEmpty(info.GrantedBalance) || info.GrantedBalance == \"0.00\"\n ? \"\"\n : $\"赠送余额: ¥{info.GrantedBalance}\");\n\n var total = double.Parse(info.TotalBalance);\n if (total <= 0)\n msg += \"\\n\\n⚠️ 余额不足,请及时充值\";\n\n _host.ShowThought(\"💰 DeepSeek 余额\", msg.TrimEnd());\n }\n catch (HttpRequestException ex)\n {\n _host.ShowThought(\"❌ 查询失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n _host.ShowThought(\"❌ 查询失败\", ex.Message);\n }\n }\n\n /// <summary>发送两次极短请求来演示缓存命中率</summary>\n private async Task TestCache()\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n _host.ShowThought(\"⚠️ 未配置\", \"请先右键 → DeepSeek → 设置 API Key\");\n return;\n }\n\n try\n {\n _host.ShowThought(\"⚡ 缓存测试\", \"正在发送第 1 次请求…\");\n\n var body = JsonSerializer.Serialize(new\n {\n model = \"deepseek-chat\",\n messages = new[] { new { role = \"user\", content = \"Hello\" } },\n max_tokens = 1\n });\n\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n var content = new StringContent(body, Encoding.UTF8, \"application/json\");\n\n // 第 1 次请求(缓存未命中)\n var resp1 = await client.PostAsync(ChatUrl, content);\n var json1 = await resp1.Content.ReadAsStringAsync();\n var usage1 = ParseUsage(json1);\n\n _host.ShowThought(\"⚡ 缓存测试\",\n \"第 1 次请求完成,正在发送第 2 次…\");\n\n // 第 2 次请求(相同前缀,应命中缓存)\n var resp2 = await client.PostAsync(ChatUrl, content);\n var json2 = await resp2.Content.ReadAsStringAsync();\n var usage2 = ParseUsage(json2);\n\n // 计算缓存命中率\n var hitRate1 = usage1.TotalInput > 0\n ? (double)usage1.CacheHit / usage1.TotalInput * 100 : 0;\n var hitRate2 = usage2.TotalInput > 0\n ? (double)usage2.CacheHit / usage2.TotalInput * 100 : 0;\n\n var msg = $\"【第 1 次请求】\\n\" +\n $\" 输入 tokens: {usage1.TotalInput}\\n\" +\n $\" 缓存命中: {usage1.CacheHit}\\n\" +\n $\" 缓存未命中: {usage1.CacheMiss}\\n\" +\n $\" 命中率: {hitRate1:F1}%\\n\\n\" +\n $\"【第 2 次请求(相同前缀)】\\n\" +\n $\" 输入 tokens: {usage2.TotalInput}\\n\" +\n $\" 缓存命中: {usage2.CacheHit}\\n\" +\n $\" 缓存未命中: {usage2.CacheMiss}\\n\" +\n $\" 命中率: {hitRate2:F1}%\\n\\n\" +\n $\"💡 第 2 次命中率应明显高于第 1 次\\n\" +\n $\"(前提是两次请求在缓存 TTL 内)\";\n\n _host.ShowThought(\"⚡ DeepSeek 缓存测试\", msg);\n }\n catch (HttpRequestException ex)\n {\n _host.ShowThought(\"❌ 测试失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n _host.ShowThought(\"❌ 测试失败\", ex.Message);\n }\n }\n\n /// <summary>从 chat completion 响应中提取 usage 字段</summary>\n private static UsageInfo ParseUsage(string json)\n {\n try\n {\n using var doc = JsonDocument.Parse(json);\n var usage = doc.RootElement.GetProperty(\"usage\");\n var hit = usage.TryGetProperty(\"prompt_cache_hit_tokens\", out var h) ? h.GetInt64() : 0;\n var miss = usage.TryGetProperty(\"prompt_cache_miss_tokens\", out var m) ? m.GetInt64() : 0;\n var input = usage.TryGetProperty(\"prompt_tokens\", out var p) ? p.GetInt64() : 0;\n var output = usage.TryGetProperty(\"completion_tokens\", out var c) ? c.GetInt64() : 0;\n return new UsageInfo(input, output, hit, miss);\n }\n catch\n {\n return new UsageInfo(0, 0, 0, 0);\n }\n }\n\n private record UsageInfo(long Input, long Output, long CacheHit, long CacheMiss)\n {\n public long TotalInput => CacheHit + CacheMiss;\n }\n}\n\n// ── API 响应模型 ──\n\npublic class BalanceResponse\n{\n [JsonPropertyName(\"is_available\")]\n public bool IsAvailable { get; set; }\n\n [JsonPropertyName(\"balance_infos\")]\n public List<BalanceInfo>? BalanceInfos { get; set; }\n}\n\npublic class BalanceInfo\n{\n [JsonPropertyName(\"currency\")]\n public string Currency { get; set; } = \"CNY\";\n\n [JsonPropertyName(\"total_balance\")]\n public string TotalBalance { get; set; } = \"0.00\";\n\n [JsonPropertyName(\"granted_balance\")]\n public string GrantedBalance { get; set; } = \"0.00\";\n\n [JsonPropertyName(\"topped_up_balance\")]\n public string ToppedUpBalance { get; set; } = \"0.00\";\n}\n"}, "f8c2bdbc2dd9ba6a8103cf204f87d7e86b50c7a9": {"t": 1785756823.909881, "n": 7829, "h": "6acdf83e356f255b", "c": "using System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing MyPersonalTool.Sdk;\n\nnamespace DeepSeekPlugin;\n\n/// <summary>\n/// DeepSeek 余额查询 &amp; 缓存测试插件\n/// 首次使用需通过右键菜单设置 API Key\n/// </summary>\n[Plugin(\"DeepSeek 查询\", Version = \"1.1.0\", Description = \"查询 DeepSeek API 余额、用量和缓存命中率\")]\npublic class DeepSeekQueryPlugin : PluginBase\n{\n private const string ConfigKey = \"deepseek_api_key\";\n private const string BalanceUrl = \"https://api.deepseek.com/user/balance\";\n private const string ChatUrl = \"https://api.deepseek.com/v1/chat/completions\";\n\n private IPluginHost? _host;\n\n public override string Name => \"DeepSeek 查询\";\n public override string Description => \"查询 DeepSeek API 余额和缓存状态\";\n\n public override async Task InitializeAsync(IPluginHost host)\n {\n _host = host;\n await base.InitializeAsync(host);\n\n // 设置 API Key(始终可用)\n host.RegisterAction(\"设置 API Key\", \"🔑\", \"⚙️\", \"DeepSeek\",\n async () =>\n {\n var currentKey = host.GetConfig(ConfigKey) ?? \"\";\n var key = await host.ShowInputDialog(\n \"🔑 DeepSeek API Key\",\n \"输入你的 DeepSeek API Key (sk-...)\",\n currentKey);\n if (string.IsNullOrEmpty(key)) return;\n\n host.SetConfig(ConfigKey, key);\n host.ShowThought(\"✅ 配置已保存\",\n \"DeepSeek API Key 已保存,现在可以使用查询功能。\");\n });\n\n // 查询余额\n host.RegisterAction(\"查询余额\", \"💰\", \"🔍\", \"DeepSeek\",\n async () => await QueryBalance());\n\n // 缓存测试\n host.RegisterAction(\"缓存命中测试\", \"⚡\", \"🔍\", \"DeepSeek\",\n async () => await TestCache());\n\n host.Log(\"DeepSeek 查询插件已加载\");\n }\n\n private string? GetApiKey() => _host?.GetConfig(ConfigKey);\n\n private async Task QueryBalance()\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n _host.ShowThought(\"⚠️ 未配置\", \"请先右键 → DeepSeek → 设置 API Key\");\n return;\n }\n\n try\n {\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n\n var json = await client.GetStringAsync(BalanceUrl);\n var resp = JsonSerializer.Deserialize<BalanceResponse>(json);\n\n if (resp?.BalanceInfos == null || resp.BalanceInfos.Count == 0)\n {\n _host.ShowThought(\"💰 余额\", \"暂未获取到余额信息\");\n return;\n }\n\n var info = resp.BalanceInfos[0];\n var available = resp.IsAvailable ? \"✅ 可用\" : \"❌ 不可用\";\n var msg = $\"状态: {available}\\n\" +\n $\"余额: ¥{info.TotalBalance}\\n\" +\n (string.IsNullOrEmpty(info.ToppedUpBalance) || info.ToppedUpBalance == \"0.00\"\n ? \"\"\n : $\"充值余额: ¥{info.ToppedUpBalance}\\n\") +\n (string.IsNullOrEmpty(info.GrantedBalance) || info.GrantedBalance == \"0.00\"\n ? \"\"\n : $\"赠送余额: ¥{info.GrantedBalance}\");\n\n var total = double.Parse(info.TotalBalance);\n if (total <= 0)\n msg += \"\\n\\n⚠️ 余额不足,请及时充值\";\n\n _host.ShowThought(\"💰 DeepSeek 余额\", msg.TrimEnd());\n }\n catch (HttpRequestException ex)\n {\n _host.ShowThought(\"❌ 查询失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n _host.ShowThought(\"❌ 查询失败\", ex.Message);\n }\n }\n\n /// <summary>发送两次极短请求来演示缓存命中率</summary>\n private async Task TestCache()\n {\n if (_host == null) return;\n var apiKey = GetApiKey();\n\n if (string.IsNullOrEmpty(apiKey))\n {\n _host.ShowThought(\"⚠️ 未配置\", \"请先右键 → DeepSeek → 设置 API Key\");\n return;\n }\n\n try\n {\n _host.ShowThought(\"⚡ 缓存测试\", \"正在发送第 1 次请求…\");\n\n var body = JsonSerializer.Serialize(new\n {\n model = \"deepseek-chat\",\n messages = new[] { new { role = \"user\", content = \"Hello\" } },\n max_tokens = 1\n });\n\n using var client = new HttpClient();\n client.DefaultRequestHeaders.Add(\"Authorization\", $\"Bearer {apiKey}\");\n var content = new StringContent(body, Encoding.UTF8, \"application/json\");\n\n // 第 1 次请求(缓存未命中)\n var resp1 = await client.PostAsync(ChatUrl, content);\n var json1 = await resp1.Content.ReadAsStringAsync();\n var usage1 = ParseUsage(json1);\n\n _host.ShowThought(\"⚡ 缓存测试\",\n \"第 1 次请求完成,正在发送第 2 次…\");\n\n // 第 2 次请求(相同前缀,应命中缓存)\n var resp2 = await client.PostAsync(ChatUrl, content);\n var json2 = await resp2.Content.ReadAsStringAsync();\n var usage2 = ParseUsage(json2);\n\n // 计算缓存命中率\n var hitRate1 = usage1.TotalInput > 0\n ? (double)usage1.CacheHit / usage1.TotalInput * 100 : 0;\n var hitRate2 = usage2.TotalInput > 0\n ? (double)usage2.CacheHit / usage2.TotalInput * 100 : 0;\n\n var msg = $\"【第 1 次请求】\\n\" +\n $\" 输入 tokens: {usage1.TotalInput}\\n\" +\n $\" 缓存命中: {usage1.CacheHit}\\n\" +\n $\" 缓存未命中: {usage1.CacheMiss}\\n\" +\n $\" 命中率: {hitRate1:F1}%\\n\\n\" +\n $\"【第 2 次请求(相同前缀)】\\n\" +\n $\" 输入 tokens: {usage2.TotalInput}\\n\" +\n $\" 缓存命中: {usage2.CacheHit}\\n\" +\n $\" 缓存未命中: {usage2.CacheMiss}\\n\" +\n $\" 命中率: {hitRate2:F1}%\\n\\n\" +\n $\"💡 第 2 次命中率应明显高于第 1 次\\n\" +\n $\"(前提是两次请求在缓存 TTL 内)\";\n\n _host.ShowThought(\"⚡ DeepSeek 缓存测试\", msg);\n }\n catch (HttpRequestException ex)\n {\n _host.ShowThought(\"❌ 测试失败\",\n $\"网络错误: {ex.Message}\\n\\n请检查 API Key 是否正确\");\n }\n catch (Exception ex)\n {\n _host.ShowThought(\"❌ 测试失败\", ex.Message);\n }\n }\n\n /// <summary>从 chat completion 响应中提取 usage 字段</summary>\n private static UsageInfo ParseUsage(string json)\n {\n try\n {\n using var doc = JsonDocument.Parse(json);\n var usage = doc.RootElement.GetProperty(\"usage\");\n var hit = usage.TryGetProperty(\"prompt_cache_hit_tokens\", out var h) ? h.GetInt64() : 0;\n var miss = usage.TryGetProperty(\"prompt_cache_miss_tokens\", out var m) ? m.GetInt64() : 0;\n var input = usage.TryGetProperty(\"prompt_tokens\", out var p) ? p.GetInt64() : 0;\n var output = usage.TryGetProperty(\"completion_tokens\", out var c) ? c.GetInt64() : 0;\n return new UsageInfo(input, output, hit, miss);\n }\n catch\n {\n return new UsageInfo(0, 0, 0, 0);\n }\n }\n\n private record UsageInfo(long Input, long Output, long CacheHit, long CacheMiss)\n {\n public long TotalInput => CacheHit + CacheMiss;\n }\n}\n\n// ── API 响应模型 ──\n\npublic class BalanceResponse\n{\n [JsonPropertyName(\"is_available\")]\n public bool IsAvailable { get; set; }\n\n [JsonPropertyName(\"balance_infos\")]\n public List<BalanceInfo>? BalanceInfos { get; set; }\n}\n\npublic class BalanceInfo\n{\n [JsonPropertyName(\"currency\")]\n public string Currency { get; set; } = \"CNY\";\n\n [JsonPropertyName(\"total_balance\")]\n public string TotalBalance { get; set; } = \"0.00\";\n\n [JsonPropertyName(\"granted_balance\")]\n public string GrantedBalance { get; set; } = \"0.00\";\n\n [JsonPropertyName(\"topped_up_balance\")]\n public string ToppedUpBalance { get; set; } = \"0.00\";\n}\n"}}}}