feat: 全功能前后端联调完成,47/47 测试通过
前端: - 新增 DietCapturePage 独立拍照识别页 - 5种消息卡片类型完整实现(数据确认/用药/饮食/报告/快捷选项) - 任务卡片区:异常警告+数据摘要+自动折叠 - 侧滑抽屉:历史对话列表+对话管理 - 运动计划:进度卡片+创建计划+每日打卡 - 报告页:拍照/相册/PDF上传+分析 - 面板按钮补全血氧/体重录入 - UI 升级:紫色主题+动画+气泡样式 - 全部迁移 Riverpod 3.x API 后端: - 新增 _UpdateMessageTypeAndMetadata,Tool Calling 自动映射消息类型 - SSE answer 事件携带 type 字段 - 提示词优化(移除"阿福",语气规则归位) - 运动计划支持 AI 创建和打卡 测试: - 新增 full_e2e_test.py 全流程测试(认证/数据CRUD/6个Agent对话/VLM/报告)
This commit is contained in:
@@ -107,6 +107,8 @@ public static class AiChatEndpoints
|
||||
var maxIterations = 5;
|
||||
var fullResponse = "";
|
||||
var completedNormally = false;
|
||||
var messageType = "text";
|
||||
var metadata = new Dictionary<string, object>();
|
||||
|
||||
for (int i = 0; i < maxIterations; i++)
|
||||
{
|
||||
@@ -129,7 +131,7 @@ public static class AiChatEndpoints
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
fullResponse += content;
|
||||
await SseWriteAsync(http, new { action = "answer", data = content }, ct);
|
||||
await SseWriteAsync(http, new { action = "answer", data = content, type = messageType }, ct);
|
||||
}
|
||||
}
|
||||
catch (JsonException) { /* 跳过解析失败的 chunk */ }
|
||||
@@ -160,6 +162,8 @@ public static class AiChatEndpoints
|
||||
}
|
||||
await SseWriteAsync(http, new { action = "tool_result", tool = tc.Function.Name, data = toolResult }, ct);
|
||||
|
||||
_UpdateMessageTypeAndMetadata(tc.Function.Name, toolResult, ref messageType, ref metadata);
|
||||
|
||||
messages.Add(new ChatMessage { Role = "tool", Content = JsonSerializer.Serialize(toolResult, JsonOpts), ToolCallId = tc.Id });
|
||||
}
|
||||
}
|
||||
@@ -597,6 +601,40 @@ public static class AiChatEndpoints
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>根据工具调用结果更新消息类型和元数据</summary>
|
||||
private static void _UpdateMessageTypeAndMetadata(string toolName, object toolResult, ref string messageType, ref Dictionary<string, object> metadata)
|
||||
{
|
||||
switch (toolName)
|
||||
{
|
||||
case "record_health_data":
|
||||
messageType = "data_confirm";
|
||||
if (toolResult is IDictionary<string, object> resultDict)
|
||||
{
|
||||
if (resultDict.TryGetValue("type", out var type))
|
||||
metadata["type"] = type.ToString();
|
||||
if (resultDict.TryGetValue("success", out var success) && success is bool b && b)
|
||||
metadata["success"] = true;
|
||||
}
|
||||
break;
|
||||
case "manage_medication":
|
||||
messageType = "medication_confirm";
|
||||
if (toolResult is IDictionary<string, object> medDict)
|
||||
{
|
||||
if (medDict.TryGetValue("name", out var name))
|
||||
metadata["name"] = name.ToString();
|
||||
if (medDict.TryGetValue("dosage", out var dosage))
|
||||
metadata["dosage"] = dosage.ToString();
|
||||
}
|
||||
break;
|
||||
case "estimate_food_text":
|
||||
messageType = "diet_analysis";
|
||||
break;
|
||||
case "analyze_report":
|
||||
messageType = "report_analysis";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>压缩图片到合理大小供 VLM API 使用</summary>
|
||||
private static void CompressImage(string inputPath, string outputPath, int maxWidth, long quality)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user