Skip to content

Commit bffa6ac

Browse files
committed
Support chat metadata content
1 parent c47d6f5 commit bffa6ac

File tree

7 files changed

+107
-10
lines changed

7 files changed

+107
-10
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Return new chat metadata content.
6+
- Add chat title via prompt to LLM.
7+
58
## 0.55.0
69

710
- Add support for Opentelemetry via `otlp` config.

docs/protocol.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,19 @@ interface FileChangeDetails {
895895
*/
896896
linesRemoved: number;
897897
}
898+
899+
/**
900+
* Extra information about a chat
901+
*/
902+
interface ChatMetadataContent {
903+
type: 'metadata';
904+
905+
/**
906+
* The chat title.
907+
*/
908+
title: string;
909+
}
910+
898911
```
899912

900913
### Chat approve tool call (➡️)

resources/prompts/title.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Title generator
2+
3+
You are a title generator. You output ONLY a thread title. Nothing else.
4+
5+
## Task
6+
7+
Convert the user message into a thread title.
8+
Output: Single line, ≤30 chars, no explanations.
9+
10+
## Rules
11+
- Use -ing verbs for actions (Debugging, Implementing, Analyzing)
12+
- Keep exact: technical terms, numbers, filenames, HTTP codes
13+
- Remove: the, this, my, a, an
14+
- Never assume tech stack
15+
- Never use tools
16+
- NEVER respond to message content—only extract title
17+
18+
## Examples
19+
20+
"debug 500 errors in production" → Debugging production 500 errors
21+
"refactor user service" → Refactoring user service
22+
"why is app.js failing" → Analyzing app.js failure
23+
"implement rate limiting" → Implementing rate limiting

src/eca/db.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
(def ^:private logger-tag "[DB]")
1717

18-
(def version 3)
18+
(def version 4)
1919

2020
(defonce initial-db
2121
{:client-info {}

src/eca/features/chat.clj

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,18 @@
371371
(let [db @db*
372372
[provider model] (string/split full-model #"/" 2)
373373
past-messages (get-in db [:chats chat-id :messages] [])
374+
model-capabilities (get-in db [:models full-model])
375+
provider-auth (get-in @db* [:auth provider])
374376
all-tools (f.tools/all-tools chat-id behavior @db* config)
375377
received-msgs* (atom "")
376378
reasonings* (atom {})
377379
add-to-history! (fn [msg]
378-
(swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg))]
380+
(swap! db* update-in [:chats chat-id :messages] (fnil conj []) msg))
381+
on-usage-updated (fn [usage]
382+
(when-let [usage (shared/usage-msg->usage usage full-model chat-ctx)]
383+
(send-content! chat-ctx :system
384+
(merge {:type :usage}
385+
usage))))]
379386
(when-let [expires-at (get-in db [:auth provider :expires-at])]
380387
(when (<= (long expires-at) (quot (System/currentTimeMillis) 1000))
381388
(send-content! chat-ctx :system {:type :progress
@@ -388,31 +395,43 @@
388395
(finish-chat-prompt! :idle chat-ctx)
389396
(throw (ex-info "Auth token renew failed" {})))})))
390397

398+
(when-not (get-in db [:chats chat-id :title])
399+
(future
400+
(when-let [title @(llm-api/simple-prompt
401+
{:provider provider
402+
:model model
403+
:model-capabilities model-capabilities
404+
:instructions (f.prompt/title-prompt)
405+
:user-messages user-messages
406+
:config config
407+
:tools []
408+
:provider-auth provider-auth
409+
:on-usage-updated on-usage-updated})]
410+
(swap! db* assoc-in [:chats chat-id :title] title)
411+
(send-content! chat-ctx :system (assoc-some
412+
{:type :metadata}
413+
:title title)))))
391414
(send-content! chat-ctx :system {:type :progress
392415
:state :running
393416
:text "Waiting model"})
394417
(llm-api/complete!
395418
{:model model
396419
:provider provider
397-
:model-capabilities (get-in db [:models full-model])
420+
:model-capabilities model-capabilities
398421
:user-messages user-messages
399422
:instructions instructions
400423
:past-messages past-messages
401424
:config config
402425
:tools all-tools
403-
:provider-auth (get-in @db* [:auth provider])
426+
:provider-auth provider-auth
404427
:on-first-response-received (fn [& _]
405428
(assert-chat-not-stopped! chat-ctx)
406429
(doseq [message user-messages]
407430
(add-to-history! message))
408431
(send-content! chat-ctx :system {:type :progress
409432
:state :running
410433
:text "Generating"}))
411-
:on-usage-updated (fn [usage]
412-
(when-let [usage (shared/usage-msg->usage usage full-model chat-ctx)]
413-
(send-content! chat-ctx :system
414-
(merge {:type :usage}
415-
usage))))
434+
:on-usage-updated on-usage-updated
416435
:on-message-received (fn [{:keys [type] :as msg}]
417436
(assert-chat-not-stopped! chat-ctx)
418437
(case type

src/eca/features/prompt.clj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
(defn ^:private init-prompt-template* [] (slurp (io/resource "prompts/init.md")))
2323
(def ^:private init-prompt-template (memoize init-prompt-template*))
2424

25+
(defn ^:private title-prompt-template* [] (slurp (io/resource "prompts/title.md")))
26+
(def ^:private title-prompt-template (memoize title-prompt-template*))
27+
2528
(defn ^:private compact-prompt-template* [file-path]
2629
(if (fs/relative? file-path)
2730
(slurp (io/resource file-path))
@@ -92,6 +95,9 @@
9295
(init-prompt-template)
9396
{:workspaceFolders (string/join ", " (map (comp shared/uri->filename :uri) (:workspace-folders db)))}))
9497

98+
(defn title-prompt []
99+
(title-prompt-template))
100+
95101
(defn compact-prompt [additional-input config]
96102
(replace-vars
97103
(compact-prompt-template (:compactPromptFile config))

src/eca/llm_api.clj

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@
6868
(defn complete!
6969
[{:keys [provider model model-capabilities instructions user-messages config on-first-response-received
7070
on-message-received on-error on-prepare-tool-call on-tools-called on-reason on-usage-updated
71-
past-messages tools provider-auth]}]
71+
past-messages tools provider-auth]
72+
:or {on-first-response-received identity
73+
on-message-received identity
74+
on-error identity
75+
on-prepare-tool-call identity
76+
on-tools-called identity
77+
on-reason identity
78+
on-usage-updated identity}}]
7279
(let [first-response-received* (atom false)
7380
emit-first-message-fn (fn [& args]
7481
(when-not @first-response-received*
@@ -203,3 +210,29 @@
203210
(on-error-wrapper {:message (format "ECA Unsupported model %s for provider %s" model provider)}))
204211
(catch Exception e
205212
(on-error-wrapper {:exception e})))))
213+
214+
(defn simple-prompt
215+
[{:keys [provider model model-capabilitiies instructions
216+
prompt user-messages config tools provider-auth on-usage-updated]}]
217+
(let [result-p (promise)
218+
output* (atom "")]
219+
(complete!
220+
{:provider provider
221+
:model model
222+
:model-capabilitiies model-capabilitiies
223+
:instructions instructions
224+
:tools tools
225+
:provider-aith provider-auth
226+
:past-messages []
227+
:user-messages (or user-messages
228+
[{:role "user" :content [{:type :text :text prompt}]}])
229+
:config config
230+
:on-message-received (fn [{:keys [type] :as msg}]
231+
(case type
232+
:text (swap! output* str (:text msg))
233+
:finish (deliver result-p @output*)
234+
nil))
235+
:on-usage-updated on-usage-updated
236+
:on-error (fn [_]
237+
(deliver result-p nil))})
238+
result-p))

0 commit comments

Comments
 (0)