Skip to content

Commit 51b13d9

Browse files
committed
Support resume command
1 parent aa5a8d5 commit 51b13d9

File tree

6 files changed

+214
-136
lines changed

6 files changed

+214
-136
lines changed

deps.edn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
borkdude/dynaload {:mvn/version "0.3.5"}
88
babashka/fs {:mvn/version "0.5.26"}
99
babashka/process {:mvn/version "0.6.23"}
10+
com.cognitect/transit-clj {:mvn/version "1.0.333"}
1011
hato/hato {:mvn/version "1.0.0"}
1112
org.slf4j/slf4j-simple {:mvn/version "2.0.17"}
1213
com.googlecode.java-diff-utils/diffutils {:mvn/version "1.3.0"}

src/eca/db.clj

Lines changed: 88 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1-
(ns eca.db)
1+
(ns eca.db
2+
(:require
3+
[babashka.fs :as fs]
4+
[clojure.java.io :as io]
5+
[clojure.string :as string]
6+
[cognitect.transit :as transit]
7+
[eca.config :as config :refer [get-env get-property]]
8+
[eca.logger :as logger]
9+
[eca.shared :as shared])
10+
(:import
11+
[java.io OutputStream]))
212

313
(set! *warn-on-reflection* true)
414

5-
(def ^:private one-million 1000000)
15+
(def ^:private logger-tag "[DB]")
16+
17+
(def version 1)
618

719
(defonce initial-db
820
{:client-info {}
@@ -11,75 +23,80 @@
1123
:chats {}
1224
:chat-behaviors ["agent" "plan"]
1325
:chat-default-behavior "agent"
14-
:models {"o4-mini" {:tools true
15-
:web-search false
16-
:reason? true
17-
:input-token-cost (/ 1.10 one-million)
18-
:output-token-cost (/ 4.40 one-million)}
19-
"o3" {:tools true
20-
:web-search false
21-
:reason? true
22-
:input-token-cost (/ 2.0 one-million)
23-
:output-token-cost (/ 8.0 one-million)}
24-
"gpt-4.1" {:tools true
25-
:web-search true
26-
:reason? false
27-
:max-output-tokens 32000
28-
:input-token-cost (/ 2.0 one-million)
29-
:output-token-cost (/ 8.0 one-million)}
30-
"gpt-5" {:tools true
31-
:web-search true
32-
:reason? true
33-
:max-output-tokens 32000
34-
:input-token-cost (/ 1.25 one-million)
35-
:output-token-cost (/ 10.0 one-million)}
36-
"gpt-5-mini" {:tools true
37-
:web-search true
38-
:reason? true
39-
:max-output-tokens 32000
40-
:input-token-cost (/ 0.25 one-million)
41-
:output-token-cost (/ 2.0 one-million)}
42-
"gpt-5-nano" {:tools true
43-
:web-search true
44-
:reason? true
45-
:max-output-tokens 32000
46-
:input-token-cost (/ 0.05 one-million)
47-
:output-token-cost (/ 0.4 one-million)}
48-
"claude-sonnet-4-0" {:tools true
49-
:web-search true
50-
:max-output-tokens 8196
51-
:reason? true
52-
:reason-tokens 2048
53-
:input-token-cost (/ 3.0 one-million)
54-
:input-cache-creation-token-cost (/ 3.75 one-million)
55-
:input-cache-read-token-cost (/ 0.3 one-million)
56-
:output-token-cost (/ 15.0 one-million)}
57-
"claude-opus-4-0" {:tools true
58-
:web-search true
59-
:max-output-tokens 8196
60-
:reason? true
61-
:reason-tokens 2048
62-
:input-token-cost (/ 15.0 one-million)
63-
:input-cache-creation-token-cost (/ 18.75 one-million)
64-
:input-cache-read-token-cost (/ 1.5 one-million)
65-
:output-token-cost (/ 75.0 one-million)}
66-
"claude-opus-4-1" {:tools true
67-
:web-search true
68-
:max-output-tokens 8196
69-
:reason? true
70-
:reason-tokens 2048
71-
:input-token-cost (/ 15.0 one-million)
72-
:input-cache-creation-token-cost (/ 18.75 one-million)
73-
:input-cache-read-token-cost (/ 1.5 one-million)
74-
:output-token-cost (/ 75.0 one-million)}
75-
"claude-3-5-haiku-latest" {:tools true
76-
:web-search true
77-
:reason? false
78-
:max-output-tokens 4096
79-
:input-token-cost (/ 0.8 one-million)
80-
:input-cache-creation-token-cost (/ 1.0 one-million)
81-
:input-cache-read-token-cost (/ 0.08 one-million)
82-
:output-token-cost (/ 4.0 one-million)}} ;; + ollama local models + custom provider models
26+
:models {}
8327
:mcp-clients {}})
8428

8529
(defonce db* (atom initial-db))
30+
31+
(defn ^:private no-flush-output-stream [^OutputStream os]
32+
(proxy [java.io.BufferedOutputStream] [os]
33+
(flush [])
34+
(close []
35+
(let [^java.io.BufferedOutputStream this this]
36+
(proxy-super flush)
37+
(proxy-super close)))))
38+
39+
(defn ^:private global-cache-dir []
40+
(let [cache-home (or (get-env "XDG_CACHE_HOME")
41+
(io/file (get-property "user.home") ".cache"))]
42+
(io/file cache-home "eca")))
43+
44+
(defn ^:private workspaces-hash
45+
"Return an 8-char base64 (URL-safe, no padding) key for the given
46+
workspace set."
47+
[workspaces]
48+
(let [paths (->> workspaces
49+
(map #(str (fs/absolutize (fs/file (shared/uri->filename (:uri %))))))
50+
(distinct)
51+
(sort))
52+
joined (string/join ":" paths)
53+
md (java.security.MessageDigest/getInstance "SHA-256")
54+
digest (.digest (doto md (.update (.getBytes joined "UTF-8"))))
55+
encoder (-> (java.util.Base64/getUrlEncoder)
56+
(.withoutPadding))
57+
key (.encodeToString encoder digest)]
58+
(subs key 0 (min 8 (count key)))))
59+
60+
(defn ^:private transit-global-db-file [workspaces]
61+
(io/file (global-cache-dir) (workspaces-hash workspaces) "db.transit.json"))
62+
63+
(defn ^:private read-cache [cache-file]
64+
(try
65+
(logger/logging-task
66+
:db/read-cache
67+
(if (fs/exists? cache-file)
68+
(let [cache (with-open [is (io/input-stream cache-file)]
69+
(transit/read (transit/reader is :json)))]
70+
(when (= version (:version cache))
71+
cache))
72+
(logger/info logger-tag (str "No existing DB cache found for " cache-file))))
73+
(catch Throwable e
74+
(logger/error logger-tag "Could not load global cache from DB" e))))
75+
76+
(defn ^:private upsert-cache! [cache cache-file]
77+
(try
78+
(logger/logging-task
79+
:db/upsert-cache
80+
(io/make-parents cache-file)
81+
;; https://github.com/cognitect/transit-clj/issues/43
82+
(with-open [os ^OutputStream (no-flush-output-stream (io/output-stream cache-file))]
83+
(let [writer (transit/writer os :json)]
84+
(transit/write writer cache))))
85+
(catch Throwable e
86+
(logger/error logger-tag (str "Could not upsert db cache to " cache-file) e))))
87+
88+
(defn ^:private read-workspaces-cache [workspaces]
89+
(let [cache (read-cache (transit-global-db-file workspaces))]
90+
(when (= version (:version cache))
91+
cache)))
92+
93+
(defn load-db-from-cache! [db*]
94+
(when-let [global-cache (read-workspaces-cache (:workspace-folders @db*))]
95+
(swap! db* (fn [state-db]
96+
(merge state-db
97+
(select-keys global-cache [:chats]))))))
98+
99+
(defn update-workspaces-cache! [db]
100+
(-> (select-keys db [:chats])
101+
(assoc :version version)
102+
(upsert-cache! (transit-global-db-file (:workspace-folders db)))))

0 commit comments

Comments
 (0)