https://github.com/brianium/cog-town
Build agentic workflows with simple core.async primitives
https://github.com/brianium/cog-town
agents async channels clojure llms
Last synced: 3 months ago
JSON representation
Build agentic workflows with simple core.async primitives
- Host: GitHub
- URL: https://github.com/brianium/cog-town
- Owner: brianium
- License: mit
- Created: 2025-05-16T19:13:17.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-07-04T11:25:40.000Z (11 months ago)
- Last Synced: 2026-03-02T07:50:14.960Z (3 months ago)
- Topics: agents, async, channels, clojure, llms
- Language: Clojure
- Homepage:
- Size: 236 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Cog Town 🏘️
[](https://clojars.org/com.github.brianium/cog-town)
Build **agentic workflows** in Clojure with the ergonomics of `core.async`.
`cog.town` gives you a tiny set of composable primitives—**cogs**—stateful, concurrent agents that pass messages over channels.
Think of them as Lego® bricks for conversational or multimodal AI systems.
Build things like:
- [Conversational personas](dev/workflows/conversation.clj)
- [Debates you can listen to](dev/workflows/debate.clj)
- [Multimodal agents who can speak and show you things](dev/workflows/multimodal.clj)
The dev environment and sample workflows use OpenAI's models. In order to run the samples, your environment
should have an OPENAI_API_KEY variable containing a valid OpenaAI API key.
## 5-min API tour (sans llms)
```clojure
(ns my.ns
(require [clojure.core.async :as a]
[clojure.string :as string]
[cog.town :as cogs]))
;;; 1. Create some cogs
(def echo
(cogs/cog [] (fn [ctx msg]
(let [resp (str "👋 you said: " msg)]
(-> (conj ctx msg)
(conj resp)
(vector resp))))))
(def shout
(cogs/cog [] (fn [ctx msg]
(let [resp (clojure.string/upper-case msg)]
(-> (conj ctx msg)
(conj resp)
(vector resp))))))
(a/put! shout "hello!")
(a/take! shout println) ;;; => HELLO!
;;; cogs can be dereferenced to get a live snapshot of their context
@shout
;; => ["hello!", "HELLO!"]
;;; 2. Wire cogs into a flow
(def shout-flow (cogs/flow [echo shout]))
(a/put! shout-flow "hello!")
(a/take! shout-flow println)
(a/close! shout-flow)
;;; 3. Let two cogs talk
(def shout-convo (cogs/dialogue echo shout))
(a/put! shout-convo "hello!")
(a/go-loop []
(when-some [msg (a/ (:output response) first :message :content first :output-text :text)
:format (when-some [fmt (some-> (:text response) :format)]
(if (some? (:json-schema fmt))
:json-schema
:text))}]
[(conj log-entries output-entry) output-entry]))
```
vs.
```clojure
(defn gpt-4o
"Less pure. Context is backed by some protocol using next.jdbc or similar"
[context input]
(let [log-entries (ctx/insert! context input)
response (openai/create-response :model :gpt-4o :easy-input-messages log-entries)
output-entry {:role :assistant
:content (-> (:output response) first :message :content first :output-text :text)
:format (when-some [fmt (some-> (:text response) :format)]
(if (some? (:json-schema fmt))
:json-schema
:text))}]
(ctx/insert! context output-entry)
[context output-entry]))
```
Clojure is freedom.
---
## Observing & time‑travel
* **Live snapshot** – since a cog implements `IDeref`, you can simply do `(@my-cog)` to get the latest context value.
* **Audit / replay** – Optionally collect the pair `[ctx out]` in transition functions, storing it in an atom or log.
* **Forking** – Pick any historical `ctx` value and feed it back into a new cog for “what‑if” exploration.
---
## Performance & GC notes
* Contexts **≤ 2 MB** updated a few times per second are generally safe.
* Watch `-Xlog:gc*` or `jstat -gcutil` during dev; full GCs should be rare.
* If context grows (large transcripts, embeddings), store bulky data off‑heap or behind an ID reference and keep lightweight keys in `ctx`.
* Cap the length of any time‑travel timeline vector or store *deltas* to avoid memory blow‑up.
---
## API reference (cheatsheet)
```clojure
(cog ctx transition & [buf-or-n xf ex-handler]) => Cog
(fork cog) => Cog
(fork cog ctx-fn) => Cog
(fork cog ctx-fn io) => Cog
(fork cog ctx-fn io transition-fn) => Cog
(extend cog io & [transition-fn]) => Cog
(flow [ch1 ch2 …] & opts) => IoChannel
(fanout chs & opts) => IoChannel
(gate trigger-ch & opts) => IoChannel
(dialogue cogA cogB & opts) => IoChannel
@cog => Context
(:*context cog) => Atom
```
For detailed doc‑strings run:
```clojure
(clojure.repl/doc cog.town/cog)
```
---
## License
MIT © 2025 Brian Scaturro