Is there a better way to do JS for await, than constructing a JS function like this?
(defn collect-lm-response [response]
(let [collector (js/Function. "response"
"return (async () => {
const chunks = [];
for await (const chunk of response.text) {
chunks.push(chunk);
}
return chunks.join('');
})();")]
(collector response)))
(defn create-memory-from-context! [context]
(p/let [[model] (vscode/lm.selectChatModels
#js {:vendor "copilot" :family "gpt-4o"})]
(when model
(let [prompt (str "Create memory entry from: " context)
messages [(vscode/LanguageModelChatMessage.User prompt)]
token-source (vscode/CancellationTokenSource.)]
(p/let [response (.sendRequest model (clj->js messages)
#js {} (.-token token-source))
result (collect-lm-response response)]
{:success true :memory result})))))
It’s when I collect the response streamed from an AI agent, where the examples use for await…@pez Have you tried p/doseq?
Will try that. Thanks!
hmm, no p/doseq works on sync iterables of promises. chatgpt gave me this hint:
(defn consume-async-iterable [it]
(let [iter (.values it)]
(p/loop []
(p/let [res (.next iter)]
(if (.-done res)
(js/console.log "Done!")
(do
(js/console.log "Value:" (.-value res))
(p/recur)))))))Thanks. IMO better than a JS string 😃
I’ll give it a try later today.
I don't think this works either, but the hint that an asynciterable delivers elements asynchronously is new to me. I should support for await in squint, before I thought it could just be written as for + an inner await.
Cool! Should SCI also support this, or can something be added to promesa that would make it more accessible to achieve. Dunno if p/doseq can be extended to support it?
it would be nice if p/doseq could be extended
here is a squint example of how to deal with an asyncIterable on a lower level: https://squint-cljs.github.io/squint/?src=KGRlZm4gZXhlYyBbeF0KICAoanMvUHJvbWlzZS5yZXNvbHZlIHgpKQoKKGRlZm4gXjphc3luYyBeOmdlbiBnZW4gW10KICAoanMteWllbGQgKGV4ZWMgMSkpCiAgKGpzLXlpZWxkIChleGVjIDIpKQogIG5pbCkKCihkZWZuIF46YXN5bmMgaXRlcmF0ZSBbXQogIChsZXQgW2cgKGdlbikKICAgICAgICBpdGVyICgoYWdldCBnIGpzL1N5bWJvbC5hc3luY0l0ZXJhdG9yKSkKICAgICAgICByZXN1bHRzIChhdG9tIFtdKV0KICAgIChsb29wIFtdCiAgICAgIChsZXQgW3YgKGpzLWF3YWl0ICgubmV4dCBpdGVyKSldCiAgICAgICAgKGlmIHYuZG9uZSBAcmVzdWx0cwogICAgICAgICAgKGRvIChzd2FwISByZXN1bHRzIGNvbmogdi52YWx1ZSkKICAgICAgICAgICAgKHJlY3VyKSkpKSkpKQoKKGpzLWF3YWl0IChpdGVyYXRlKSk%3D I think this can be translated to lower level promesa as well
This might work:
(defn iterate []
(let [g (gen)
iter ((aget g js/Symbol.asyncIterator))
results (atom [])]
(p/loop []
(p/let [v (.next iter)]
(if (.-done v) @results
(do (swap! results conj (.-value v))
(p/recur)))))))it returns a promise with the iterated values
g is the async stream here
maybe you need p/recur btw
instead of just recur
(edited)
@pez Finally figured out the SCI version. When you copy/paste this in a SCI env with promesa it works:
(def my-fn (js/Function "return (async function* makeAsyncIterable() {
for (let i = 1; i <= 3; i++) {
await new Promise(r => setTimeout(r, 1000));
yield i;
}
})"))
(def async-generator ((my-fn)))
(defn iterate [generator]
(let [iter (.call (unchecked-get generator js/Symbol.asyncIterator) generator)
results (atom [])]
(js/console.log iter)
(p/loop []
(p/let [v (.next iter)]
(if (.-done v) @results
(do
(js/console.log (.-value v))
(swap! results conj (.-value v))
(p/recur)))))))
(p/let [my-values (iterate async-generator)]
(prn my-values))note that iterate contains some js/console.logs that you may want to get rid of
Amazing. Maybe I can add to joyride.core?
Is async-iterate a good name?
(defn consume-lm-response [response]
(p/let [chunks (async-iterate (.-text response))]
(apply str chunks)))I don't think iterate is a good name since this function just collects all of them in one go
async-iterator-seq or so?
Where do I see console.logs emitted from the joyride repl?
wait i got it, it's the vscode dev tools
Yes. I think println goes to the Calva repl output, if the Calva repl is connected.
I am actually planning on making it more predictable where some of the output goes. But js/console.log I won’t try to meddle with.