fix: VLM识别修复 + 饮食CRUD + 侧边栏美化 + UI优化

- VLM: ChatMessage.Content string→object, 修复视觉content双重序列化
- 饮食: diet/medication端点 record→手动JSON解析, 修复循环引用
- 饮食记录: 左滑删除 + 去评分 + AI饮食评语(DeepSeek)
- 侧边栏: 功能区统一Row+Expanded, 服务包compact模式
- 历史对话: 点击加载历史消息
- 登录页: 居中布局, 去底部空白
- UI: 主色加深#6C5CE7, maxWidth:1024防图片超大
This commit is contained in:
MingNian
2026-06-04 16:27:03 +08:00
parent c44917b8e9
commit b944a31983
12 changed files with 413 additions and 305 deletions

View File

@@ -265,33 +265,40 @@ public static class AiChatEndpoints
using (var stream = new FileStream(filePath, FileMode.Create))
await file.CopyToAsync(stream, ct);
var compressedPath = Path.Combine(uploadsDir, $"compressed_{safeName}");
CompressImage(filePath, compressedPath, maxWidth: 1024, quality: 90L);
var compressedBytes = await File.ReadAllBytesAsync(compressedPath, ct);
var base64 = Convert.ToBase64String(compressedBytes);
// 千问3.7-plus + vl_high_resolution_images=true 支持到 16M 像素,保留原图细节
// base64 ≥ 7MB官方限制时才压缩否则原图上传
var fileBytes = await File.ReadAllBytesAsync(filePath, ct);
var base64 = Convert.ToBase64String(fileBytes);
if (base64.Length > 7 * 1024 * 1024)
{
var compressedPath = Path.Combine(uploadsDir, $"compressed_{safeName}");
CompressImage(filePath, compressedPath, maxWidth: 2048, quality: 85L);
fileBytes = await File.ReadAllBytesAsync(compressedPath, ct);
base64 = Convert.ToBase64String(fileBytes);
}
imageUrls.Add($"data:image/jpeg;base64,{base64}");
}
var prompt = """
2JSON数组
[{"name":"名称","portion":"份量","calories":热量}]
西
""";
JSON数组
[{"name":"名称","portion":"份量","calories":热量}]
JSON
""";
try
{
var response = await visionClient.VisionAsync(prompt, imageUrls, userText: "请看图识别食物", maxTokens: 8192, ct: ct);
var result = response.Choices?.FirstOrDefault()?.Message?.Content ?? "{}";
// 记录VLM原始返回用于排查
var uploadsDir2 = Path.Combine(Directory.GetCurrentDirectory(), "uploads");
var logPath = Path.Combine(uploadsDir2, $"vlm_log_{DateTime.Now:HHmmss}.txt");
await File.WriteAllTextAsync(logPath, $"MODEL: {Environment.GetEnvironmentVariable("VLM_MODEL")}\nIMAGE_SIZE: {imageUrls.FirstOrDefault()?.Length ?? 0}\nRESPONSE:\n{result}", ct);
return Results.Ok(new { code = 0, data = result, message = (string?)null });
}
catch (Exception ex)
{
return Results.Ok(new { code = 50001, data = (object?)null, message = $"食物识别失败:{ex.Message}" });
}
try
{
var response = await visionClient.VisionAsync(prompt, imageUrls, userText: null, maxTokens: 8192, ct: ct);
var result = response.Choices?.FirstOrDefault()?.Message?.Content ?? "{}";
// 记录VLM原始返回用于排查
var uploadsDir2 = Path.Combine(Directory.GetCurrentDirectory(), "uploads");
var logPath = Path.Combine(uploadsDir2, $"vlm_log_{DateTime.Now:HHmmss}.txt");
await File.WriteAllTextAsync(logPath, $"MODEL: {Environment.GetEnvironmentVariable("VLM_MODEL")}\nIMAGE_SIZE: {imageUrls.FirstOrDefault()?.Length ?? 0}\nRESPONSE:\n{result}", ct);
return Results.Ok(new { code = 0, data = result, message = (string?)null });
}
catch (Exception ex)
{
return Results.Ok(new { code = 50001, data = (object?)null, message = $"食物识别失败:{ex.Message}" });
}
});
}