Phase I - Session Blackboard + READ Layer: - SessionBlackboardService with Postgres-Only cache - DataProfileService for data overview generation - PicoInferenceService for LLM-driven PICO extraction - Frontend DataContextCard and VariableDictionaryPanel - E2E tests: 31/31 passed Phase II - Conversation Layer LLM + Intent Router: - ConversationService with SSE streaming - IntentRouterService (rule-first + LLM fallback, 6 intents) - SystemPromptService with 6-segment dynamic assembly - TokenTruncationService for context management - ChatHandlerService as unified chat entry - Frontend SSAChatPane and useSSAChat hook - E2E tests: 38/38 passed Phase III - Method Consultation + AskUser Standardization: - ToolRegistryService with Repository Pattern - MethodConsultService with DecisionTable + LLM enhancement - AskUserService with global interrupt handling - Frontend AskUserCard component - E2E tests: 13/13 passed Phase IV - Dialogue-Driven Analysis + QPER Integration: - ToolOrchestratorService (plan/execute/report) - analysis_plan SSE event for WorkflowPlan transmission - Dual-channel confirmation (ask_user card + workspace button) - PICO as optional hint for LLM parsing - E2E tests: 25/25 passed R Statistics Service: - 5 new R tools: anova_one, baseline_table, fisher, linear_reg, wilcoxon - Enhanced guardrails and block helpers - Comprehensive test suite (run_all_tools_test.js) Documentation: - Updated system status document (v5.9) - Updated SSA module status and development plan (v1.8) Total E2E: 107/107 passed (Phase I: 31, Phase II: 38, Phase III: 13, Phase IV: 25) Co-authored-by: Cursor <cursoragent@cursor.com>
87 lines
2.8 KiB
R
87 lines
2.8 KiB
R
# utils/block_helpers.R
|
||
# Block-based 输出协议 — 构造函数
|
||
#
|
||
# 所有 R 工具通过这些函数构建 report_blocks[],
|
||
# 前端 DynamicReport.tsx 根据 block.type 统一渲染。
|
||
# 支持 4 种 Block 类型:markdown / table / image / key_value
|
||
|
||
#' 构造 Markdown 文本块
|
||
#' @param content Markdown 格式文本(支持标题、列表、加粗等)
|
||
#' @param title 可选标题(前端渲染为区块标题)
|
||
#' @return block list
|
||
make_markdown_block <- function(content, title = NULL) {
|
||
block <- list(type = "markdown", content = content)
|
||
if (!is.null(title)) block$title <- title
|
||
block
|
||
}
|
||
|
||
#' 构造表格块
|
||
#' @param headers 列名字符向量,如 c("组别", "均值", "标准差")
|
||
#' @param rows 行数据列表,每行为字符向量,如 list(c("A", "5.2", "1.3"), ...)
|
||
#' @param title 可选表格标题
|
||
#' @param footnote 可选脚注(如方法说明)
|
||
#' @return block list
|
||
make_table_block <- function(headers, rows, title = NULL, footnote = NULL, metadata = NULL) {
|
||
block <- list(
|
||
type = "table",
|
||
headers = as.list(headers),
|
||
rows = lapply(rows, as.list)
|
||
)
|
||
if (!is.null(title)) block$title <- title
|
||
if (!is.null(footnote)) block$footnote <- footnote
|
||
if (!is.null(metadata)) block$metadata <- metadata
|
||
block
|
||
}
|
||
|
||
#' 从 data.frame 构造表格块(便捷方法)
|
||
#' @param df data.frame
|
||
#' @param title 可选表格标题
|
||
#' @param footnote 可选脚注
|
||
#' @param digits 数值列保留小数位数,默认 3
|
||
#' @return block list
|
||
make_table_block_from_df <- function(df, title = NULL, footnote = NULL, digits = 3) {
|
||
headers <- colnames(df)
|
||
|
||
rows <- lapply(seq_len(nrow(df)), function(i) {
|
||
lapply(df[i, , drop = FALSE], function(val) {
|
||
if (is.numeric(val)) {
|
||
format(round(val, digits), nsmall = digits)
|
||
} else {
|
||
as.character(val)
|
||
}
|
||
})
|
||
})
|
||
|
||
make_table_block(headers, rows, title = title, footnote = footnote)
|
||
}
|
||
|
||
#' 构造图片块
|
||
#' @param base64_data 完整的 data URI,如 "data:image/png;base64,..."
|
||
#' @param title 可选图片标题
|
||
#' @param alt 可选 alt 文本(无障碍 + Word 导出用)
|
||
#' @return block list
|
||
make_image_block <- function(base64_data, title = NULL, alt = NULL) {
|
||
block <- list(type = "image", data = base64_data)
|
||
if (!is.null(title)) block$title <- title
|
||
if (!is.null(alt)) block$alt <- alt
|
||
block
|
||
}
|
||
|
||
#' 构造键值对块
|
||
#' @param items 命名列表或 list(list(key=..., value=...), ...)
|
||
#' @param title 可选标题
|
||
#' @return block list
|
||
make_kv_block <- function(items, title = NULL) {
|
||
if (!is.null(names(items)) && length(names(items)) > 0 && names(items)[1] != "") {
|
||
kv_list <- lapply(names(items), function(k) {
|
||
list(key = k, value = as.character(items[[k]]))
|
||
})
|
||
} else {
|
||
kv_list <- items
|
||
}
|
||
|
||
block <- list(type = "key_value", items = kv_list)
|
||
if (!is.null(title)) block$title <- title
|
||
block
|
||
}
|