mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
release: v1.9.0
This commit is contained in:
@@ -48,6 +48,35 @@ pub fn load_memory_source_candidates(
|
||||
Ok(candidates)
|
||||
}
|
||||
|
||||
pub fn load_session_memory_source_candidates(
|
||||
conn: &Connection,
|
||||
session_id: &str,
|
||||
limit: usize,
|
||||
min_message_length: usize,
|
||||
) -> Result<Vec<MemorySourceCandidate>, String> {
|
||||
let rows = AgentDao::list_message_text_rows_by_session_id(conn, session_id, limit)
|
||||
.map_err(|e| format!("读取 session agent_messages 失败: {e}"))?;
|
||||
let mut candidates = Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
|
||||
for row in rows {
|
||||
push_candidate(
|
||||
&mut candidates,
|
||||
&mut seen,
|
||||
row.session_id,
|
||||
row.role,
|
||||
row.content,
|
||||
row.timestamp_ms,
|
||||
min_message_length,
|
||||
);
|
||||
}
|
||||
|
||||
candidates.sort_by(|a, b| b.created_at.cmp(&a.created_at));
|
||||
candidates.truncate(limit);
|
||||
|
||||
Ok(candidates)
|
||||
}
|
||||
|
||||
fn load_unified_general_candidates(
|
||||
conn: &Connection,
|
||||
from_timestamp: Option<i64>,
|
||||
@@ -189,7 +218,7 @@ fn format_sqlite_datetime(timestamp_ms: i64) -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::load_memory_source_candidates;
|
||||
use super::{load_memory_source_candidates, load_session_memory_source_candidates};
|
||||
use rusqlite::{params, Connection};
|
||||
|
||||
fn create_test_schema(conn: &Connection) {
|
||||
@@ -368,4 +397,59 @@ mod tests {
|
||||
assert!(session_ids.contains(&"agent-1"));
|
||||
assert!(!session_ids.contains(&"legacy-only"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_session_memory_source_candidates_only_reads_target_session() {
|
||||
let conn = Connection::open_in_memory().expect("open in memory db");
|
||||
create_test_schema(&conn);
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO agent_sessions (id, model, created_at, updated_at) VALUES (?1, ?2, ?3, ?4)",
|
||||
params![
|
||||
"agent-1",
|
||||
"claude-sonnet-4",
|
||||
"2026-03-12T10:00:00+08:00",
|
||||
"2026-03-12T10:00:00+08:00"
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
conn.execute(
|
||||
"INSERT INTO agent_sessions (id, model, created_at, updated_at) VALUES (?1, ?2, ?3, ?4)",
|
||||
params![
|
||||
"agent-2",
|
||||
"claude-sonnet-4",
|
||||
"2026-03-12T10:05:00+08:00",
|
||||
"2026-03-12T10:05:00+08:00"
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO agent_messages (session_id, role, content_json, timestamp) VALUES (?1, ?2, ?3, ?4)",
|
||||
params![
|
||||
"agent-1",
|
||||
"user",
|
||||
r#"[{"type":"text","text":"请记住我现在更偏好 pnpm 工作流"}]"#,
|
||||
"2026-03-12T10:00:00+08:00"
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
conn.execute(
|
||||
"INSERT INTO agent_messages (session_id, role, content_json, timestamp) VALUES (?1, ?2, ?3, ?4)",
|
||||
params![
|
||||
"agent-2",
|
||||
"assistant",
|
||||
r#"[{"type":"text","text":"这条消息不该进入 agent-1 的自动抽取"}]"#,
|
||||
"2026-03-12T10:05:00+08:00"
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let candidates = load_session_memory_source_candidates(&conn, "agent-1", 10, 1)
|
||||
.expect("load session candidates");
|
||||
|
||||
assert_eq!(candidates.len(), 1);
|
||||
assert_eq!(candidates[0].session_id, "agent-1");
|
||||
assert!(candidates[0].content.contains("pnpm"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1156,11 +1156,39 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_request_telemetry_fixture(root: &Path) {
|
||||
let request_logs_dir = root.join("request_logs");
|
||||
fs::create_dir_all(&request_logs_dir).expect("create request logs dir");
|
||||
|
||||
let mut log = lime_infra::telemetry::RequestLog::new(
|
||||
"req-analysis-1".to_string(),
|
||||
lime_core::ProviderType::OpenAI,
|
||||
"gpt-5.4".to_string(),
|
||||
false,
|
||||
);
|
||||
log.session_id = Some("session-1".to_string());
|
||||
log.thread_id = Some("thread-1".to_string());
|
||||
log.turn_id = Some("turn-1".to_string());
|
||||
log.pending_request_id = Some("req-1".to_string());
|
||||
log.queued_turn_id = Some("queued-1".to_string());
|
||||
log.mark_success(320, 200);
|
||||
|
||||
fs::write(
|
||||
request_logs_dir.join("requests_2026-03-27.jsonl"),
|
||||
format!(
|
||||
"{}\n",
|
||||
serde_json::to_string(&log).expect("serialize request log")
|
||||
),
|
||||
)
|
||||
.expect("write request log");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_export_runtime_analysis_handoff_to_workspace() {
|
||||
let temp_dir = TempDir::new().expect("temp dir");
|
||||
let detail = build_detail();
|
||||
let thread_read = build_thread_read();
|
||||
write_request_telemetry_fixture(temp_dir.path());
|
||||
|
||||
let result = export_runtime_analysis_handoff(&detail, &thread_read, temp_dir.path())
|
||||
.expect("export");
|
||||
@@ -1189,7 +1217,8 @@ mod tests {
|
||||
assert!(brief.contains("外部分析交接简报"));
|
||||
assert!(brief.contains("pending request:1"));
|
||||
assert!(brief.contains("证据关联与可观测覆盖"));
|
||||
assert!(brief.contains("requestTelemetry (unlinked)"));
|
||||
assert!(brief.contains("requestTelemetry"));
|
||||
assert!(!brief.contains("requestTelemetry (unlinked)"));
|
||||
assert!(brief.contains("/workspace/lime"));
|
||||
|
||||
let context = fs::read_to_string(context_path).expect("context");
|
||||
@@ -1199,6 +1228,7 @@ mod tests {
|
||||
assert!(context.contains("\"observability\""));
|
||||
assert!(context.contains("\"correlationKeys\""));
|
||||
assert!(context.contains("\"gapSignals\""));
|
||||
assert!(context.contains("\"matchedRequestCount\": 1"));
|
||||
assert!(context.contains("/workspace/lime"));
|
||||
assert!(!context.contains(temp_dir.path().to_string_lossy().as_ref()));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,7 @@
|
||||
use crate::agent::SessionDetail;
|
||||
use crate::commands::aster_agent_cmd::AgentRuntimeThreadReadModel;
|
||||
use crate::services::runtime_evidence_pack_service::{
|
||||
build_runtime_observability_summary_json, export_runtime_evidence_pack,
|
||||
RuntimeEvidencePackExportResult,
|
||||
export_runtime_evidence_pack, RuntimeEvidencePackExportResult,
|
||||
};
|
||||
use crate::services::runtime_handoff_artifact_service::{
|
||||
export_runtime_handoff_bundle, RuntimeHandoffBundleExportResult,
|
||||
@@ -145,12 +144,7 @@ pub fn export_runtime_replay_case(
|
||||
let recent_artifacts = collect_recent_artifact_paths(detail);
|
||||
let pending_requests = collect_pending_request_inputs(detail, thread_read);
|
||||
let recent_timeline = collect_recent_timeline_items(detail);
|
||||
let observability_summary = build_runtime_observability_summary_json(
|
||||
detail,
|
||||
thread_read,
|
||||
&recent_artifacts,
|
||||
&evidence_pack.known_gaps,
|
||||
);
|
||||
let observability_summary = read_observability_summary_from_evidence_pack(&evidence_pack)?;
|
||||
let success_criteria = build_success_criteria(
|
||||
detail,
|
||||
thread_read,
|
||||
@@ -283,6 +277,29 @@ fn write_replay_file(
|
||||
})
|
||||
}
|
||||
|
||||
fn read_observability_summary_from_evidence_pack(
|
||||
evidence_pack: &RuntimeEvidencePackExportResult,
|
||||
) -> Result<Value, String> {
|
||||
let runtime_path = Path::new(&evidence_pack.pack_absolute_root).join("runtime.json");
|
||||
let raw = fs::read_to_string(&runtime_path).map_err(|error| {
|
||||
format!(
|
||||
"读取 evidence pack runtime.json 失败 {}: {error}",
|
||||
runtime_path.display()
|
||||
)
|
||||
})?;
|
||||
let payload = serde_json::from_str::<Value>(raw.as_str()).map_err(|error| {
|
||||
format!(
|
||||
"解析 evidence pack runtime.json 失败 {}: {error}",
|
||||
runtime_path.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(payload
|
||||
.pointer("/observabilitySummary")
|
||||
.cloned()
|
||||
.unwrap_or(Value::Null))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_input_json(
|
||||
detail: &SessionDetail,
|
||||
@@ -1261,6 +1278,7 @@ mod tests {
|
||||
assert!(input.contains("\"pending_request\""));
|
||||
assert!(input.contains("\"observability\""));
|
||||
assert!(input.contains("\"requestTelemetry\""));
|
||||
assert!(!input.contains("\"artifactValidator\""));
|
||||
|
||||
let expected = fs::read_to_string(expected_path).expect("expected");
|
||||
assert!(expected.contains("不要要求与原始会话完全相同的工具调用顺序"));
|
||||
@@ -1274,6 +1292,6 @@ mod tests {
|
||||
assert!(links.contains("\"handoffBundle\""));
|
||||
assert!(links.contains("\"evidencePack\""));
|
||||
assert!(links.contains("\"observabilitySummary\""));
|
||||
assert!(links.contains("\"artifactValidator\""));
|
||||
assert!(!links.contains("\"artifactValidator\""));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user