update lexicon
This commit is contained in:
@@ -19,3 +19,4 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
dirs = "5.0"
|
dirs = "5.0"
|
||||||
|
chrono = "0.4.44"
|
||||||
|
|||||||
74
docs/DOCS.md
74
docs/DOCS.md
@@ -2,32 +2,72 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
MCP server for AI memory. Reads/writes core.md and memory.md. Nothing more.
|
MCP server for AI memory. Reads/writes core.json and memory/*.json in atproto lexicon record format.
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
- AI decides, tool records
|
- AI decides, tool records
|
||||||
- File I/O only, no database
|
- File I/O only, no database
|
||||||
- 4 MCP tools: read_core, read_memory, save_memory, compress
|
- 4 MCP tools: read_core, read_memory, save_memory, compress
|
||||||
- Simple, unbreakable, long-lasting
|
- Storage format: atproto getRecord JSON
|
||||||
|
|
||||||
## MCP Tools
|
## MCP Tools
|
||||||
|
|
||||||
| Tool | Args | Description |
|
| Tool | Args | Description |
|
||||||
|------|------|-------------|
|
|------|------|-------------|
|
||||||
| read_core | none | Returns core.md content |
|
| read_core | none | Returns core.json record |
|
||||||
| read_memory | none | Returns memory.md content |
|
| read_memory | none | Returns latest memory record |
|
||||||
| save_memory | content: string | Overwrites memory.md |
|
| save_memory | content: string | Creates new memory record (version increments) |
|
||||||
| compress | conversation: string | Reads memory.md + conversation, writes compressed result to memory.md |
|
| compress | conversation: string | Same as save_memory (AI compresses before calling) |
|
||||||
|
|
||||||
compress note: AI decides what to keep/discard. Tool just writes.
|
compress note: AI decides what to keep/discard. Tool just writes.
|
||||||
|
|
||||||
## Data
|
## Data
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.config/aigpt/
|
~/Library/Application Support/ai.syui.gpt/ (macOS)
|
||||||
├── core.md ← read only (identity, settings)
|
~/.local/share/ai.syui.gpt/ (Linux)
|
||||||
└── memory.md ← read/write (memories, grows over time)
|
├── core.json ← read only, rkey: self
|
||||||
|
└── memory/
|
||||||
|
├── {tid1}.json ← version 1
|
||||||
|
├── {tid2}.json ← version 2
|
||||||
|
└── {tid3}.json ← version 3 (latest)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Record Format
|
||||||
|
|
||||||
|
core (single record, rkey: self):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"uri": "at://{did}/ai.syui.gpt.core/self",
|
||||||
|
"value": {
|
||||||
|
"$type": "ai.syui.gpt.core",
|
||||||
|
"did": "did:plc:xxx",
|
||||||
|
"handle": "ai.syui.ai",
|
||||||
|
"content": {
|
||||||
|
"$type": "ai.syui.gpt.core#markdown",
|
||||||
|
"text": "personality and instructions"
|
||||||
|
},
|
||||||
|
"createdAt": "2025-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
memory (multiple records, rkey: tid):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"uri": "at://{did}/ai.syui.gpt.memory/{tid}",
|
||||||
|
"value": {
|
||||||
|
"$type": "ai.syui.gpt.memory",
|
||||||
|
"did": "did:plc:xxx",
|
||||||
|
"content": {
|
||||||
|
"$type": "ai.syui.gpt.memory#markdown",
|
||||||
|
"text": "# Memory\n\n## ..."
|
||||||
|
},
|
||||||
|
"version": 5,
|
||||||
|
"createdAt": "2026-03-01T12:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
@@ -35,8 +75,8 @@ compress note: AI decides what to keep/discard. Tool just writes.
|
|||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── mcp/server.rs ← JSON-RPC over stdio
|
├── mcp/server.rs ← JSON-RPC over stdio
|
||||||
├── core/reader.rs ← read core.md, memory.md
|
├── core/reader.rs ← read core.json, memory/*.json
|
||||||
├── core/writer.rs ← write memory.md
|
├── core/writer.rs ← write memory/{tid}.json
|
||||||
└── main.rs ← CLI + MCP server
|
└── main.rs ← CLI + MCP server
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -46,20 +86,20 @@ When compress is called, AI should:
|
|||||||
- Keep facts and decisions
|
- Keep facts and decisions
|
||||||
- Discard procedures and processes
|
- Discard procedures and processes
|
||||||
- Resolve contradictions (keep newer)
|
- Resolve contradictions (keep newer)
|
||||||
- Don't duplicate core.md content
|
- Don't duplicate core.json content
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
aigpt serve # start MCP server
|
aigpt server # start MCP server
|
||||||
aigpt read-core # CLI: read core.md
|
aigpt read-core # CLI: read core.json
|
||||||
aigpt read-memory # CLI: read memory.md
|
aigpt read-memory # CLI: read latest memory
|
||||||
aigpt save-memory "content" # CLI: write memory.md
|
aigpt save-memory "..." # CLI: create new memory record
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tech
|
## Tech
|
||||||
|
|
||||||
- Rust, MCP (JSON-RPC over stdio), file I/O only
|
- Rust, MCP (JSON-RPC over stdio), atproto record format, file I/O only
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
|
|||||||
117
src/core/config.rs
Normal file
117
src/core/config.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub path: Option<String>,
|
||||||
|
pub did: Option<String>,
|
||||||
|
pub handle: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_file() -> PathBuf {
|
||||||
|
dirs::config_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join("ai.syui.gpt")
|
||||||
|
.join("config.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Config {
|
||||||
|
let path = config_file();
|
||||||
|
match fs::read_to_string(&path) {
|
||||||
|
Ok(content) => serde_json::from_str(&content).unwrap_or(defaults()),
|
||||||
|
Err(_) => defaults(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn defaults() -> Config {
|
||||||
|
Config {
|
||||||
|
path: None,
|
||||||
|
did: None,
|
||||||
|
handle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
let cfg_path = config_file();
|
||||||
|
if !cfg_path.exists() {
|
||||||
|
if let Some(parent) = cfg_path.parent() {
|
||||||
|
let _ = fs::create_dir_all(parent);
|
||||||
|
}
|
||||||
|
let default_cfg = json!({
|
||||||
|
"path": null,
|
||||||
|
"did": null,
|
||||||
|
"handle": null
|
||||||
|
});
|
||||||
|
let _ = fs::write(&cfg_path, serde_json::to_string_pretty(&default_cfg).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let cfg = load();
|
||||||
|
let core_path = record_path(&cfg, "ai.syui.gpt.core", "self");
|
||||||
|
if !core_path.exists() {
|
||||||
|
if let Some(parent) = core_path.parent() {
|
||||||
|
let _ = fs::create_dir_all(parent);
|
||||||
|
}
|
||||||
|
let did = cfg.did.clone().unwrap_or_else(|| "self".to_string());
|
||||||
|
let handle = cfg.handle.clone().unwrap_or_else(|| "self".to_string());
|
||||||
|
let now = Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string();
|
||||||
|
let core_record = json!({
|
||||||
|
"uri": format!("at://{}/ai.syui.gpt.core/self", did),
|
||||||
|
"value": {
|
||||||
|
"$type": "ai.syui.gpt.core",
|
||||||
|
"did": did,
|
||||||
|
"handle": handle,
|
||||||
|
"content": {
|
||||||
|
"$type": "ai.syui.gpt.core#markdown",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"createdAt": now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _ = fs::write(&core_path, serde_json::to_string_pretty(&core_record).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory_dir = collection_dir(&cfg, "ai.syui.gpt.memory");
|
||||||
|
let _ = fs::create_dir_all(&memory_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_dir(cfg: &Config) -> PathBuf {
|
||||||
|
match &cfg.path {
|
||||||
|
Some(p) => {
|
||||||
|
if p.starts_with("~/") {
|
||||||
|
dirs::home_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join(&p[2..])
|
||||||
|
} else {
|
||||||
|
PathBuf::from(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => dirs::config_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join("ai.syui.gpt"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identity(cfg: &Config) -> String {
|
||||||
|
if cfg!(windows) {
|
||||||
|
cfg.handle.clone().unwrap_or_else(|| "self".to_string())
|
||||||
|
} else {
|
||||||
|
cfg.did.clone().unwrap_or_else(|| "self".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// $cfg/{did|handle}/{collection}/{rkey}.json
|
||||||
|
pub fn record_path(cfg: &Config, collection: &str, rkey: &str) -> PathBuf {
|
||||||
|
base_dir(cfg)
|
||||||
|
.join(identity(cfg))
|
||||||
|
.join(collection)
|
||||||
|
.join(format!("{}.json", rkey))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// $cfg/{did|handle}/{collection}/
|
||||||
|
pub fn collection_dir(cfg: &Config, collection: &str) -> PathBuf {
|
||||||
|
base_dir(cfg).join(identity(cfg)).join(collection)
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
pub mod config;
|
||||||
pub mod reader;
|
pub mod reader;
|
||||||
pub mod writer;
|
pub mod writer;
|
||||||
|
|||||||
@@ -1,24 +1,37 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use serde_json::Value;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
fn config_dir() -> PathBuf {
|
use crate::core::config;
|
||||||
dirs::config_dir()
|
|
||||||
.unwrap_or_else(|| PathBuf::from("."))
|
pub fn read_core() -> Result<Value> {
|
||||||
.join("ai.syui.gpt")
|
let cfg = config::load();
|
||||||
|
let path = config::record_path(&cfg, "ai.syui.gpt.core", "self");
|
||||||
|
let content = fs::read_to_string(&path)
|
||||||
|
.with_context(|| format!("Failed to read {}", path.display()))?;
|
||||||
|
let record: Value = serde_json::from_str(&content)
|
||||||
|
.with_context(|| format!("Failed to parse {}", path.display()))?;
|
||||||
|
Ok(record)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_core() -> Result<String> {
|
pub fn read_memory() -> Result<Option<Value>> {
|
||||||
let path = config_dir().join("core.md");
|
let cfg = config::load();
|
||||||
fs::read_to_string(&path)
|
let dir = config::collection_dir(&cfg, "ai.syui.gpt.memory");
|
||||||
.with_context(|| format!("Failed to read {}", path.display()))
|
if !dir.exists() {
|
||||||
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
let mut files: Vec<_> = fs::read_dir(&dir)?
|
||||||
pub fn read_memory() -> Result<String> {
|
.filter_map(|e| e.ok())
|
||||||
let path = config_dir().join("memory.md");
|
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
|
||||||
match fs::read_to_string(&path) {
|
.collect();
|
||||||
Ok(content) => Ok(content),
|
if files.is_empty() {
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(String::new()),
|
return Ok(None);
|
||||||
Err(e) => Err(e).with_context(|| format!("Failed to read {}", path.display())),
|
|
||||||
}
|
}
|
||||||
|
files.sort_by_key(|e| e.file_name());
|
||||||
|
let latest = files.last().unwrap().path();
|
||||||
|
let content = fs::read_to_string(&latest)
|
||||||
|
.with_context(|| format!("Failed to read {}", latest.display()))?;
|
||||||
|
let record: Value = serde_json::from_str(&content)
|
||||||
|
.with_context(|| format!("Failed to parse {}", latest.display()))?;
|
||||||
|
Ok(Some(record))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,59 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use chrono::Utc;
|
||||||
|
use serde_json::json;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
fn config_dir() -> PathBuf {
|
use crate::core::{config, reader};
|
||||||
dirs::config_dir()
|
|
||||||
.unwrap_or_else(|| PathBuf::from("."))
|
fn generate_tid() -> String {
|
||||||
.join("ai.syui.gpt")
|
const CHARSET: &[u8] = b"234567abcdefghijklmnopqrstuvwxyz";
|
||||||
|
let micros = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_micros() as u64;
|
||||||
|
let mut tid = [0u8; 13];
|
||||||
|
let mut v = micros;
|
||||||
|
for i in (0..13).rev() {
|
||||||
|
tid[i] = CHARSET[(v & 0x1f) as usize];
|
||||||
|
v >>= 5;
|
||||||
|
}
|
||||||
|
String::from_utf8(tid.to_vec()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_version() -> u64 {
|
||||||
|
match reader::read_memory() {
|
||||||
|
Ok(Some(record)) => record["value"]["version"].as_u64().unwrap_or(0) + 1,
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_memory(content: &str) -> Result<()> {
|
pub fn save_memory(content: &str) -> Result<()> {
|
||||||
let dir = config_dir();
|
let cfg = config::load();
|
||||||
|
let did = cfg.did.clone().unwrap_or_else(|| "self".to_string());
|
||||||
|
let tid = generate_tid();
|
||||||
|
let version = next_version();
|
||||||
|
let now = Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string();
|
||||||
|
|
||||||
|
let record = json!({
|
||||||
|
"uri": format!("at://{}/ai.syui.gpt.memory/{}", did, tid),
|
||||||
|
"value": {
|
||||||
|
"$type": "ai.syui.gpt.memory",
|
||||||
|
"did": did,
|
||||||
|
"content": {
|
||||||
|
"$type": "ai.syui.gpt.memory#markdown",
|
||||||
|
"text": content
|
||||||
|
},
|
||||||
|
"version": version,
|
||||||
|
"createdAt": now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let dir = config::collection_dir(&cfg, "ai.syui.gpt.memory");
|
||||||
fs::create_dir_all(&dir)
|
fs::create_dir_all(&dir)
|
||||||
.with_context(|| format!("Failed to create {}", dir.display()))?;
|
.with_context(|| format!("Failed to create {}", dir.display()))?;
|
||||||
let path = dir.join("memory.md");
|
let path = dir.join(format!("{}.json", tid));
|
||||||
fs::write(&path, content)
|
let json_str = serde_json::to_string_pretty(&record)?;
|
||||||
|
fs::write(&path, json_str)
|
||||||
.with_context(|| format!("Failed to write {}", path.display()))
|
.with_context(|| format!("Failed to write {}", path.display()))
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/main.rs
71
src/main.rs
@@ -1,16 +1,16 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
use aigpt::core::{reader, writer};
|
use aigpt::core::{config, reader, writer};
|
||||||
use aigpt::mcp::MCPServer;
|
use aigpt::mcp::MCPServer;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "aigpt")]
|
#[command(name = "aigpt")]
|
||||||
#[command(about = "AI memory MCP server - read/write core.md and memory.md")]
|
#[command(about = "AI memory MCP server")]
|
||||||
#[command(version)]
|
#[command(version)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Option<Commands>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
@@ -18,13 +18,13 @@ enum Commands {
|
|||||||
/// Start MCP server (JSON-RPC over stdio)
|
/// Start MCP server (JSON-RPC over stdio)
|
||||||
Server,
|
Server,
|
||||||
|
|
||||||
/// Read core.md
|
/// Read core.json
|
||||||
ReadCore,
|
ReadCore,
|
||||||
|
|
||||||
/// Read memory.md
|
/// Read latest memory record
|
||||||
ReadMemory,
|
ReadMemory,
|
||||||
|
|
||||||
/// Save content to memory.md
|
/// Create new memory record
|
||||||
SaveMemory {
|
SaveMemory {
|
||||||
/// Content to write
|
/// Content to write
|
||||||
content: String,
|
content: String,
|
||||||
@@ -32,29 +32,70 @@ enum Commands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
config::init();
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Commands::Server => {
|
None => {
|
||||||
|
print_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Commands::Server) => {
|
||||||
let server = MCPServer::new();
|
let server = MCPServer::new();
|
||||||
server.run()?;
|
server.run()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::ReadCore => {
|
Some(Commands::ReadCore) => {
|
||||||
let content = reader::read_core()?;
|
let record = reader::read_core()?;
|
||||||
print!("{}", content);
|
println!("{}", serde_json::to_string_pretty(&record)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::ReadMemory => {
|
Some(Commands::ReadMemory) => {
|
||||||
let content = reader::read_memory()?;
|
match reader::read_memory()? {
|
||||||
print!("{}", content);
|
Some(record) => println!("{}", serde_json::to_string_pretty(&record)?),
|
||||||
|
None => println!("No memory records found"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::SaveMemory { content } => {
|
Some(Commands::SaveMemory { content }) => {
|
||||||
writer::save_memory(&content)?;
|
writer::save_memory(&content)?;
|
||||||
println!("Saved to memory.md");
|
println!("Saved.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_status() {
|
||||||
|
let cfg = config::load();
|
||||||
|
let did = cfg.did.clone().unwrap_or_else(|| "self".to_string());
|
||||||
|
let handle = cfg.handle.clone().unwrap_or_else(|| "self".to_string());
|
||||||
|
let base = config::base_dir(&cfg);
|
||||||
|
let id = config::identity(&cfg);
|
||||||
|
|
||||||
|
let memory_dir = config::collection_dir(&cfg, "ai.syui.gpt.memory");
|
||||||
|
let memory_count = std::fs::read_dir(&memory_dir)
|
||||||
|
.map(|entries| {
|
||||||
|
entries
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
|
||||||
|
.count()
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
let latest_version = match reader::read_memory() {
|
||||||
|
Ok(Some(record)) => record["value"]["version"].as_u64().unwrap_or(0),
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("aigpt - AI memory MCP server\n");
|
||||||
|
println!("config: {}", config::config_file().display());
|
||||||
|
println!("did: {}", did);
|
||||||
|
println!("handle: {}", handle);
|
||||||
|
println!();
|
||||||
|
println!("path: {}/", base.display());
|
||||||
|
println!(" {}/{}", id, "ai.syui.gpt.core/self.json");
|
||||||
|
println!(" {}/{}", id, "ai.syui.gpt.memory/*.json");
|
||||||
|
println!();
|
||||||
|
println!("memory: {} records (version: {})", memory_count, latest_version);
|
||||||
|
}
|
||||||
|
|||||||
@@ -162,14 +162,15 @@ impl MCPServer {
|
|||||||
|
|
||||||
fn tool_read_core(&self) -> Value {
|
fn tool_read_core(&self) -> Value {
|
||||||
match reader::read_core() {
|
match reader::read_core() {
|
||||||
Ok(content) => json!({ "content": content }),
|
Ok(record) => record,
|
||||||
Err(e) => json!({ "error": e.to_string() }),
|
Err(e) => json!({ "error": e.to_string() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tool_read_memory(&self) -> Value {
|
fn tool_read_memory(&self) -> Value {
|
||||||
match reader::read_memory() {
|
match reader::read_memory() {
|
||||||
Ok(content) => json!({ "content": content }),
|
Ok(Some(record)) => record,
|
||||||
|
Ok(None) => json!({ "content": "" }),
|
||||||
Err(e) => json!({ "error": e.to_string() }),
|
Err(e) => json!({ "error": e.to_string() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user