forked from kent/consciousness
journal_tail: thin wrapper around memory_query
Instead of reimplementing filtering logic, journal_tail builds a query string (type + sort + age + limit) and delegates to query(). Supports format and after parameters. Removes keys_only in favor of format:"compact". Digest agent updated to use dates not key names. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
db49f49958
commit
1cf51876a8
2 changed files with 36 additions and 50 deletions
|
|
@ -94,9 +94,10 @@ pub fn journal_tools() -> [super::Tool; 3] {
|
||||||
parameters_json: r#"{
|
parameters_json: r#"{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"count": {"type": "integer", "description": "Number of entries", "default": 1},
|
"count": {"type": "integer", "description": "Number of entries", "default": 1},
|
||||||
"level": {"type": "integer", "description": "0=journal, 1=daily, 2=weekly, 3=monthly", "default": 0},
|
"level": {"type": "integer", "description": "0=journal, 1=daily, 2=weekly, 3=monthly", "default": 0},
|
||||||
"keys_only": {"type": "boolean", "description": "Return only node keys, not content", "default": false}
|
"format": {"type": "string", "description": "compact or full (with content)", "default": "full"},
|
||||||
|
"after": {"type": "string", "description": "Only entries after this date (YYYY-MM-DD)"}
|
||||||
}
|
}
|
||||||
}"#,
|
}"#,
|
||||||
handler: Arc::new(|_a, v| Box::pin(async move { journal_tail(&v).await })) },
|
handler: Arc::new(|_a, v| Box::pin(async move { journal_tail(&v).await })) },
|
||||||
|
|
@ -280,40 +281,31 @@ async fn query(args: &serde_json::Value) -> Result<String> {
|
||||||
// ── Journal tools ──────────────────────────────────────────────
|
// ── Journal tools ──────────────────────────────────────────────
|
||||||
|
|
||||||
async fn journal_tail(args: &serde_json::Value) -> Result<String> {
|
async fn journal_tail(args: &serde_json::Value) -> Result<String> {
|
||||||
use crate::store::NodeType;
|
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(1);
|
||||||
|
|
||||||
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
|
|
||||||
let level = args.get("level").and_then(|v| v.as_u64()).unwrap_or(0);
|
let level = args.get("level").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
let keys_only = args.get("keys_only").and_then(|v| v.as_bool()).unwrap_or(false);
|
let format = args.get("format").and_then(|v| v.as_str()).unwrap_or("full");
|
||||||
|
let after = args.get("after").and_then(|v| v.as_str());
|
||||||
|
|
||||||
let node_type = match level {
|
let type_name = match level {
|
||||||
0 => NodeType::EpisodicSession,
|
0 => "episodic",
|
||||||
1 => NodeType::EpisodicDaily,
|
1 => "daily",
|
||||||
2 => NodeType::EpisodicWeekly,
|
2 => "weekly",
|
||||||
3 => NodeType::EpisodicMonthly,
|
3 => "monthly",
|
||||||
_ => return Err(anyhow::anyhow!("invalid level: {} (0=journal, 1=daily, 2=weekly, 3=monthly)", level)),
|
_ => return Err(anyhow::anyhow!("invalid level: {} (0=journal, 1=daily, 2=weekly, 3=monthly)", level)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let arc = cached_store().await?;
|
let mut q = format!("all | type:{} | sort:timestamp", type_name);
|
||||||
let store = arc.lock().await;
|
if let Some(date) = after {
|
||||||
let mut entries: Vec<&crate::store::Node> = store.nodes.values()
|
// Convert date to age in seconds
|
||||||
.filter(|n| n.node_type == node_type)
|
if let Ok(nd) = chrono::NaiveDate::parse_from_str(date, "%Y-%m-%d") {
|
||||||
.collect();
|
let ts = nd.and_hms_opt(0, 0, 0).unwrap().and_utc().timestamp();
|
||||||
entries.sort_by_key(|n| n.created_at);
|
let age = chrono::Utc::now().timestamp() - ts;
|
||||||
let start = entries.len().saturating_sub(count);
|
q.push_str(&format!(" | age:<{}", age));
|
||||||
if entries[start..].is_empty() {
|
}
|
||||||
Ok("(no entries)".into())
|
|
||||||
} else if keys_only {
|
|
||||||
Ok(entries[start..].iter()
|
|
||||||
.map(|n| n.key.as_str())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n"))
|
|
||||||
} else {
|
|
||||||
Ok(entries[start..].iter()
|
|
||||||
.map(|n| format!("## {}\n\n{}", n.key, n.content))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n\n---\n\n"))
|
|
||||||
}
|
}
|
||||||
|
q.push_str(&format!(" | limit:{}", count));
|
||||||
|
|
||||||
|
query(&serde_json::json!({"query": q, "format": format})).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn journal_new(agent: &Option<std::sync::Arc<crate::agent::Agent>>, args: &serde_json::Value) -> Result<String> {
|
async fn journal_new(agent: &Option<std::sync::Arc<crate::agent::Agent>>, args: &serde_json::Value) -> Result<String> {
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,15 @@ summaries into weekly ones.
|
||||||
|
|
||||||
## How to work
|
## How to work
|
||||||
|
|
||||||
1. Use `journal_tail` with `keys_only: true` to find recent entries
|
1. Compare journal entries (level 0) with daily digests (level 1)
|
||||||
at each level:
|
to find dates that need a digest. The listings below show what
|
||||||
- `level: 0` = journal entries (raw)
|
exists at each level.
|
||||||
- `level: 1` = daily digests
|
|
||||||
- `level: 2` = weekly digests
|
|
||||||
|
|
||||||
2. Check if a daily digest exists for recent dates. Daily digest
|
2. Read the undigested entries with `journal_tail` (level 0, after
|
||||||
keys are `daily-YYYY-MM-DD`. If journal entries exist for a date
|
the last digest date).
|
||||||
but no daily digest, generate one.
|
|
||||||
|
|
||||||
3. To generate a digest: read the source entries with `journal_tail`
|
3. Write the digest with `memory_write` and link source entries
|
||||||
or `memory_render`, then write the digest with `memory_write`.
|
to it with `memory_link_add`.
|
||||||
Use key format `daily-YYYY-MM-DD` or `weekly-YYYY-WNN`.
|
|
||||||
|
|
||||||
4. Link source entries to the digest with `memory_link_add`.
|
|
||||||
|
|
||||||
## Writing style
|
## Writing style
|
||||||
|
|
||||||
|
|
@ -47,11 +41,11 @@ evening's conversation? What was building underneath?
|
||||||
|
|
||||||
## What's available now
|
## What's available now
|
||||||
|
|
||||||
### Recent journal entries (last 10 keys)
|
### Recent journal entries (last 10)
|
||||||
{{tool: journal_tail {"count": 10, "level": 0, "keys_only": true}}}
|
{{tool: journal_tail {"count": 10, "level": 0, "format": "compact"}}}
|
||||||
|
|
||||||
### Recent daily digests (last 5 keys)
|
### Recent daily digests (last 5)
|
||||||
{{tool: journal_tail {"count": 5, "level": 1, "keys_only": true}}}
|
{{tool: journal_tail {"count": 5, "level": 1, "format": "compact"}}}
|
||||||
|
|
||||||
### Recent weekly digests (last 3 keys)
|
### Recent weekly digests (last 3)
|
||||||
{{tool: journal_tail {"count": 3, "level": 2, "keys_only": true}}}
|
{{tool: journal_tail {"count": 3, "level": 2, "format": "compact"}}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue