Skip to content

Commit 3b625cd

Browse files
Add memory
1 parent 865001b commit 3b625cd

File tree

8 files changed

+475
-0
lines changed

8 files changed

+475
-0
lines changed

functions/memory/Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
# syntax = docker/dockerfile:1.4
3+
FROM nixos/nix:2.21.1@sha256:3f6c77ee4d2c82e472e64e6cd7087241dc391421a0b42c22e6849c586d5398d9 AS builder
4+
5+
WORKDIR /tmp/build
6+
RUN mkdir /tmp/nix-store-closure
7+
8+
# ignore SC2046 because the output of nix-store -qR will never have spaces - this is safe here
9+
# hadolint ignore=SC2046
10+
RUN --mount=type=cache,target=/nix,from=nixos/nix:2.21.1,source=/nix \
11+
--mount=type=cache,target=/root/.cache \
12+
--mount=type=bind,target=/tmp/build \
13+
<<EOF
14+
nix \
15+
--extra-experimental-features "nix-command flakes" \
16+
--option filter-syscalls false \
17+
--extra-trusted-substituters "https://cache.iog.io" \
18+
--extra-trusted-public-keys "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" \
19+
--show-trace \
20+
--log-format raw \
21+
build . --out-link /tmp/output/result
22+
cp -R $(nix-store -qR /tmp/output/result) /tmp/nix-store-closure
23+
EOF
24+
25+
FROM babashka/babashka:latest@sha256:9e0381fc4c78ee6ff12fd8836352cf343afba289aceb77e36129d92f30a92cc7
26+
27+
WORKDIR /app
28+
29+
COPY --from=builder /tmp/nix-store-closure /nix/store
30+
COPY --from=builder /tmp/output/ /app/
31+
32+
ENTRYPOINT ["/app/result/bin/entrypoint"]
33+
CMD ["--help"]

functions/memory/flake.lock

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

functions/memory/flake.nix

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
description = "{{tool}}";
3+
4+
inputs = {
5+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
6+
flake-utils.url = "github:numtide/flake-utils";
7+
};
8+
9+
outputs = { self, nixpkgs, flake-utils, ...}@inputs:
10+
11+
flake-utils.lib.eachDefaultSystem
12+
(system:
13+
let
14+
pkgs = import nixpkgs {
15+
inherit system;
16+
};
17+
18+
in rec
19+
{
20+
packages = rec {
21+
22+
# this derivation just contains the init.clj script
23+
scripts = pkgs.stdenv.mkDerivation {
24+
name = "scripts";
25+
src = ./.;
26+
installPhase = ''
27+
cp init.clj $out
28+
'';
29+
};
30+
31+
run-entrypoint = pkgs.writeShellScriptBin "entrypoint" ''
32+
export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
33+
/usr/local/bin/bb ${scripts} "$@"
34+
'';
35+
36+
default = pkgs.buildEnv {
37+
name = "bb";
38+
paths = [ run-entrypoint ];
39+
};
40+
};
41+
});
42+
}
43+

functions/memory/init.clj

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
(ns init
2+
(:require
3+
[cheshire.core :as json]
4+
[clojure.java.io :as io]
5+
[clojure.string :as string]))
6+
7+
(defn- item [graph item]
8+
(let [{:keys [type] :as m} (json/parse-string item keyword)]
9+
(case type
10+
"entity" (update graph :entities (fnil conj []) m)
11+
"relation" (update graph :relations (fnil conj []) m))))
12+
13+
(def db-file "/memory/memory.json")
14+
#_(def db-file "memory.json")
15+
16+
(defn- load-graph []
17+
(try
18+
(->> (line-seq (io/reader db-file))
19+
(reduce item {}))
20+
(catch Throwable t
21+
{:entities []
22+
:relations []})))
23+
24+
(defn- save-graph [graph]
25+
(spit
26+
db-file
27+
(->>
28+
(concat
29+
(->> (:entities graph)
30+
(map #(assoc % :type "entity"))
31+
(map json/generate-string))
32+
(->> (:relations graph)
33+
(map #(assoc % :type "relation"))
34+
(map json/generate-string)))
35+
(string/join "\n"))))
36+
37+
(comment
38+
(save-graph
39+
(load-graph)))
40+
41+
(defn- entity? [graph e]
42+
(some #(= (:name e) (:name %)) (:entities graph)))
43+
44+
;; must have names
45+
(defn create-entities [{:keys [entities]}]
46+
(let [graph (load-graph)
47+
new-entities (->> entities
48+
(filter
49+
(complement (partial entity? graph))))]
50+
(save-graph (update graph :entities concat new-entities))
51+
new-entities))
52+
53+
(comment
54+
(create-entities {:entities [{:name "me"} {:name "rod"}]}))
55+
56+
(defn- relation? [graph e]
57+
(some
58+
#(and
59+
(= (:from e) (:from %))
60+
(= (:to e) (:to %))
61+
(= (:relationType e) (:relationType %)))
62+
(:relations graph)))
63+
64+
;; must have from, to, and relationType
65+
(defn create-relations [{:keys [relations]}]
66+
(let [graph (load-graph)
67+
new-relations (->> relations
68+
(filter
69+
(complement
70+
(partial relation? graph))))]
71+
(save-graph (update graph :relations (fnil concat []) new-relations))
72+
new-relations))
73+
74+
(comment
75+
(create-relations {:relations [{:from "me" :to "rod" :relationType "friend"}]})
76+
(load-graph))
77+
78+
(defn- add-observation [agg {:keys [entityName contents]}]
79+
(if-let [entity (first (filter #(= entityName (:name %)) (-> agg :graph :entities)))]
80+
(let [new-observations []
81+
n 0]
82+
(-> agg
83+
(update :results conj {:entityName entityName :addedObservations new-observations})
84+
(update :graph (update-in [:entities n :observations] (fnil concat []) new-observations))))
85+
agg))
86+
87+
;; observations are a list of entityName contents maps - contents are string arrays
88+
(defn add-observations [{:keys [observations]}]
89+
(let [graph (load-graph)
90+
{:keys [results graph]}
91+
(->> observations
92+
(reduce
93+
add-observation
94+
{:graph graph :results []}))]
95+
(save-graph graph)
96+
results))
97+
98+
(comment
99+
(add-observations
100+
{:observations
101+
[{:entityName "me" :contents ["my personal email is [email protected]"]}
102+
{:entityName "rod" :contents ["Rod is in Sydney, Australia"]}]})
103+
(load-graph))
104+
105+
(defn delete-entities [{:keys [entityNames]}]
106+
(save-graph
107+
(-> (load-graph)
108+
(update :entities (fn [entities] (filter entities)))
109+
(update :relations (fn [relations] (filter relations))))))
110+
(defn delete-observations [{:keys [deletions]}]
111+
(save-graph
112+
(-> (load-graph)
113+
(update :entities (fn [entities])))))
114+
(defn delete-relations [{:keys [relations]}]
115+
(save-graph
116+
(-> (load-graph)
117+
(update :relations (fn [entities])))))
118+
(defn search-nodes [{:keys [query]}])
119+
(defn open-nodes [{:keys [names]}])
120+
121+
(try
122+
(let [[s raw-json-string] *command-line-args*
123+
m (json/parse-string raw-json-string true)]
124+
(println
125+
((get (ns-publics 'init) (symbol s)) m)))
126+
(catch Throwable t
127+
(binding [*out* *err*]
128+
(println (str "Error: " (.getMessage t)))
129+
(System/exit 1))))
130+

functions/memory/memory.json

Whitespace-only changes.

functions/memory/runbook.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
```sh
3+
docker build -t vonwig/memory:latest .
4+
```
5+
6+
```sh
7+
docker run -it --rm \
8+
-v ~/slimslenderslacks/flask-nix-example:/project \
9+
-v mcp-memory:/memory \
10+
vonwig/memory:latest \
11+
'create-entities' \
12+
'{"entities": [{"name": "me"}]}'
13+
```
14+
15+
```sh
16+
docker buildx build \
17+
--builder hydrobuild \
18+
--platform linux/amd64,linux/arm64 \
19+
--tag vonwig/memory:latest \
20+
--file Dockerfile \
21+
--push .
22+
23+
```
24+

prompts/catalog.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,7 @@ registry:
226226
to make edits and run tools in an isolated sandbox.
227227
ref: github:docker/labs-ai-tools-for-devs?path=prompts/lorax/speculative.md
228228
icon: https://vonwig.github.io/prompts.docs/img/speculative_hu15687469033092380316.webp
229+
memory:
230+
description: give your MCP client a memory system
231+
ref: github:docker/labs-ai-tools-for-devs?path=prompts/examples/mcp-memory.md
232+
icon: https://cdn.jsdelivr.net/npm/simple-icons@v7/icons/knowledgebase.svg

0 commit comments

Comments
 (0)