#multi-object ๐งต
1 messages ยท Page 1 of 1 (latest)
@shrewd skiff getting closer... but I'm getting this weird "empty spans" effect:
not sure what those robot heads are doing
most likely the 'thinking/replying' span corresponding to API requests
aka the one that has token usage attached on non-OpenAI
Yes but I've never seen more than one in a row with no tool call - normally it stops in that situation
I feel like that started happening a lot for me with ollama in one of the recent updates too. Haven't been able to dig in to see why yet. I was wondering if there was a regression in our openai implementation
Also I hit this at the end:
โ
โ๐ค 0.2s
โ ! POST "https://api.openai.com/v1/chat/completions": 400 Bad Request {
โ ! "error": {
โ ! "message": "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The fol
โ ! "type": "invalid_request_error",
โ ! "param": "messages",
โ ! "code": null
โ ! }
โ ! }
Vikram was hitting this too #1346226656162877460 message
@shrewd skiff looking at the web trace, there are in fact function calls. They just don't show in the TUI
--> my bad actually this is a problem across TUI and Web traces
ah interesting, will look into it
weird that part of the TUI trace has the same info
but then later in the same trace, it starts to drop information
Sorry that was wrong - everything seems to be the same in TUI and web
so it's NOT a TUI-specific problem after all
oh OK - you were just looking at a different region?
yeah got confused
so back to not knowing why several "thinking" spans in a row with no tool call
๐ญ
hey how come I don't get the token count?
๐
is it supposed to work with openai?
it is, but openai is returning 0 for the token usage
no idea why. maybe they don't support it for streaming, but total guess
@shrewd skiff if you want to try, it works - now have to fine tune the prompt engineering
(so that the llm intuitively figures out what to do)
might need a _help
I'm curious how this would plug into dagger llm
@shrewd skiff @rich marsh nice side effect, you don't need a separate "prompt var" system. All vars in the llm env can be used to templatize the prompt
nice. Does set-string have abilities beyond what with-prompt-var had?
gonna go hide all those Secret.plaintext errors
it's like setContainer setToyWorkspace etc
i renamed "with" to "set" to try the DX
you can actually also expand objects ๐
@acoustic radish lol, this is an interesting consequence of marrying prompt vars + llm env vars + shell vars
which ended up not working, because of course the model is looking for 'repo' and 'ctr'
mmm oops ๐
but...what if we built on that, and just made them addressable by digest?!?!?!
eh you lose readability of the prompt itself
but, cool that we could
was that in my multi obj branch?
yeah, i'm integrating it with the shell vars now
mostly works, except for that
tl;dr after every eval we diff the vars from the last vars and set any new/different ones in the llm env, too
so no /set foo bar or /with, you just foo=$(container | from alpine) and the llm sees it too
lol just realized you wrote this in a thread called "multi-obj" my bad
that's perfect - so much smoother IMO
I started using it in my demos today, noticed you already allow setting vars in /shell then using them in /with
@shrewd skiff were you able to get the LLM to use the objects properly?
I didn't have time to get back to that
on a plane now, I have 45mn to try ๐
getting there - I think it just needs a bit of prompt engineering. i'm having it generate a prompt ๐
i can commit + push what i have
oh yes please! I'll start from what you have
Any direction you'd want to me try first?
(or avoid because you're already on it?)
@acoustic radish pushed to my fork - vito/llm-multiobj (don't have perms on your branch)
none in particular - it's all super fresh, just kicking the tires for the first time really
oh oops. will fix perms
actually I thought upstream maintainers could push to forks?
do you lack perms upstream?
oh is the branch on dagger/dagger?
oops. well there's one there now
To github.com:dagger/dagger
* [new branch] llm-multiobj -> llm-multiobj
no it's on my fork, but I thought if you write permissions on the upstream, that gave you write permissions to the forks also
can delete, not sure why i can't push to shykes/llm-multiobj
that worked ๐
nice ok
pulling now
@shrewd skiff I think getting multiobj to work might be a matter of choosing the "metaphor" so that it feels natural to the LLM with minimal explanation - just name & description of the tools
for example I realized - _undo for rolling back the selection, makes it unintuitive to do sub-pipelines eg. container | with-directory /src $(git ...)
actually I guess _scratch becomes super important
maybe _start
and bring back the concept of pipeline or chain in the descriptions
@shrewd skiff getting a build error on the CLI: cmd/dagger/llm.go:147:12: pattern llm.md: no matching files found
huh thought i fixed that, 1 sec
try now (force-pushed, sry)
np I'm building from git remote, faster from the plane ๐
(remote engine on home server)
time's up I'm landing
will try again later tonight
thanks for pushing this!
np
hmmm so far results are not that great, but i might know why
if i try to do something like build $repo using $ctr, whenever it swaps from one to the other it forgets how to work with the other one
maybe the tools aren't returning the full set, and only the current value's? i remember you saying before that sometimes it extends instead of replacing
extends instead of replacing
didn't understand that sorry
you mentioned something about the set of tools including both Container and Directory if it chained from a Container to a Directory
yeah that's in the current BBI implementation
but in multiobj it's only the current object's functions + _builtins
so it needs to understand how to juggle objects
I think we don't give the llm enough to connect the dots
hmm yeah that seems like the hard part
yes. but easier than eg. teaching it dagger shell or gql imo
i'm not sure about that - those are languages, which llms are good at
i think it's doable with good results but haven't had a chance to try
I'll try a few things
the syntax part was not the issue with shell at least.
it was understanding the object model, chaining etc
so very similar but with more ground to cover because there was no 1-1 mapping of tools ever
FYI still getting the build error
dagger -m github.com/shykes/dagger@llm-multiobj -c 'engine | service llm | up'
โ .withExec(args: ["go", "list", "-f", "{{if eq .Name \"main\"}}{{.Dir}}{{end}}", "./cmd/dagger"]): Container! 0.4s
cmd/dagger/llm.go:155:12: pattern llm.md: no matching files found
@shrewd skiff in case the current multiobj strategy doesn't pan out, what's plan B? gql? shell? other?
yeah i think this is globs somewhere actually, the file is committed
trying from a local checkout in case it's a cache issue
god that version string compute andf its full git fetch... can't wait to optimize that away
@acoustic radish pushed
yeah ๐ญ can't wait for function caching
not sure - not giving up on it yet, still tinkering
even with GraphQL I ended up doing a stateful vars thing
though, maybe it could do one big tool call that both sets vars + uses them in later queries. (kinda like a SQL CTE)
i think this might be a feature, not a bug
the idea here is: no more _load, instead we load the tools for all types currently set to vars, and you use the Type@hash value to refer to them everywhere
sweet, this works without any prompting
the main downside is loading all the tools at once, not sure if that's a dealbreaker. hmm maybe. it caches well, at least
But there is a hard limit on number of tools
And even before the limit, it might hurt performance
but, we could optimize this later maybe
you use the Type@hash value to refer to them everywhere
Don't understand this part
lemme see if i can work backward from where I ended up and keep one-toolset-at-a-time, there were other changes along the way
Feels like we're reinventing swap memory ๐
lol
basically being consistent in printing Type@hash everywhere and accepting it as argument values in place of IDs, etc.
the llm seems to pick up how it works pretty well without any prompting
ah I see, so could be a solution to this ๐
yeah, assuming the model is able to keep track of the hash values. not sure if that's reliable enough
- // +ignore=["*", ".*", "!/cmd/dagger/*", "!**/go.sum", "!**/go.mod", "!**/*.go", "!**.graphql"]
+ // +ignore=["*", ".*", "!cmd/dagger/*", "!**/go.sum", "!**/go.mod", "!**/*.go", "!**.graphql"]
source *dagger.Directory,
Oh noooo - sorry you got bit by that
perf is really variable, hard to tell why but the # of tools is prob a good guess
https://asciinema.org/a/VPSLzOVjc20n1CunBdHf0o7uo
(also claude seems generally slower than gpt-4o)
gpt-4o was much more fun
https://asciinema.org/a/JJQxdHa8XLU3c0CSKeyn5rnOL
https://v3.dagger.cloud/dagger/traces/f32918df8f8d7d2277b40b4719e1b074
pushed to vito/llm-multiobj-all-tools for now
this ^ showed a concerning issue though where it treated the container as if it were mutable, instead of chaining
@shrewd skiff in latest multi-object branch, looks like you can't set variables to a module
$ dagger llm
* /shell
โ workspace=$(github.com/shykes/toy-programmer/toy-workspace) 3.2s
โ workspace=$(github.com/shykes/toy-programmer/toy-workspace) 3.2s
! input: llm.withPrompt.setToyWorkspace load: Call: Query has no such field: "toyWorkspace"
โ โ load module 0.5s
โ
โ โ toyWorkspace: ToyWorkspace! 1.1s
โ
โ โ Llm.setToyWorkspace(
โ โ โ name: "workspace"
โ โ โ value: โ toyWorkspace: ToyWorkspace! 1.1s
โ โ ): Llm! 0.0s
โ ! load: Call: Query has no such field: "toyWorkspace"
ah good catch, will look into it tomorrow!
oh interesting - it ran those tools in parallel, that's why it didn't chain
I seem to be getting decent results with gpt-4o after going back to the model of selecting one toolset at a time ๐
renaming _load to _selectTools seems to yield even better results, but n=2
also thinking of letting _selectTools take a regex of tool names to enable, and having one of the other tools provide a simple list of all the fields (without descriptions). Maybe not necessary, but seems fun to try
โ โ 5.0s โ LLM Input Tokens: 63,957 โ LLM Output Tokens: 132
๐ฑ holy moly. that came from swapping to toyWorkspace?!
๐ค 1.5s โ LLM Input Tokens: 9,237 โ LLM Output Tokens: 31
โ โ ๐ค๐ป _selectTools map[name:ToyWorkspace@xxh3:d0f1e35037b98313] 0.0s
โ
โ๐ค The ToyWorkspace@xxh3:d0f1e35037b98313 workspace has several functions that we can explore. Here's a brief overview of its capabilities:
โ โ
โ โ 1. Read: It can read files with a specified path.
โ โ 2. Write: It can write to files by providing a path and content.
โ โ 3. Build: It can build the code at the current directory in the workspace.
โ โ
โ โ Please let me know if there's anything specific you'd like to do with this workspace, such as reading a specific file, writing data, or building the
โ โ code!
โ โ 5.9s โ LLM Input Tokens: 62,358 โ LLM Output Tokens: 121
that's weird. I don't see how!
yeah there's only like 3 tools in that one
probably something goofy
unless you have incredibly long descriptions
@acoustic radish ok if I force-push now that it's back to one toolset at a time?
(to llm-multiobj)
// "what is in a word?" I pondered. To read - such a crucial, important word. it all started when..
func read()
yeah I haven't touched that branch today
ok done - modules should work now
would recommend rolling with the prompt var <-> shell var <-> llm var alignment for now, it seems to work pretty well, and I dig that it makes the prompt even more explicit about what it's working with (e.g. if a var gets re-assigned)
one potential risk is the model losing track of the fact that Directory@xxh3:... is "repo" but seems OK so far. not sure if the model sees the original name for prompt vars or if it's just a text substition
here's how I've been testing:
/shell ctr=$(container | from golang)
/shell repo=$(git https://github.com/vito/booklit | head | tree)
use $ctr to build $repo
ah I think _load/_selectTools is dumping the entire object into the tool response. will fix
pushed, + some tweaks to tool descriptions that help out. now when it sees "run unit tests against Bass@xxh3:..." it'll jump right to selectTools[Bass@xxh3:...] without having to look anything up
will try tonight!
can it still set variables?
it might get confused juggling too many IDs
i haven't removed it, but there's no llm => shell env syncing yet. haven't seen it mess up IDs yet, but i've only done micro tests. should probably start trying more complicated tasks next
also don't think i've ever seen it call _save
i'm starting to wonder if we can get away with just one builtin, _load/_selectTools (depending on which name it likes)
but, there may be somewhere that a name <-> hash mapping is useful
yeah I saw it use save yesterday
i think vars will be useful for "returning" values but also as lightweight memory. ie compact the context, might forget IDs but not vars
ah yeah makes sense
@acoustic radish I noticed it tends to do this: Container.withExec(["obviously", "doesnt", "work"]) => "I did it! ๐" (not realizing it's lazy) - I remember there being a sync call somewhere at one point, but can't find it now on either llm or llm-multiobj. Did ya have an idea for that? Maybe we should just always sync against syncable tool call results?
I saw an explicit call to sync, in bbi/flat. assumed you added it?
grr not sure why my grep didn't find that
it was there already, i just fixed it. in the process of adding it for multiobj now
@shrewd skiff depends if we can give it a concept of starting/ending a pipeline maybe. but yeah maybe too ambitious for now
i have setting vars working, just all my demos are blowing up cause of that ๐
houston, we have moo-off ๐ https://asciinema.org/a/MaoDDLTfUY2HgK1G4wthTbpum
ha ha nice ๐
@shrewd skiff do I have to resolve the variable in the shell? Or can the llm figure out what variables are available and use them?
Since it knows how to set variables I guess it knows how to get them too
I get why you're making sure expanded vars work. But I would prefer not to have to teach every developer how to do that trick as a pre-requisite to basic prompting
nah it figures it out either way https://asciinema.org/a/HmSrmWz5gpwa5x7UbgurgXHrG
nice
i feel like it's more natural to type $foo though
less risk of ambiguity in the prompt
it just so happens that it expands atm, and the expanded form also works
Yeah in cases where you want to designate a specific var, but there may be cases where you don't want to, or can't (because the vars were dynamically set for example)
a few learnings in this one: https://asciinema.org/a/H0O0Yk9tL61oG7dJXvYLKXFWD
- the model didn't intuit that "bass" referred to a variable without a symbol prefix
- the model understood the expanded
$bass(expected) - there's no way to say or escape
$bassliterally (users prob wouldn't want to do this anyway) - using
@basswas just enough of a hint, but might as well just type$bassat that point - it grabbed the entire stdout of the tests which added 30k to the token cost... twice ๐ฌ
i'll go ahead and push as-is
@shrewd skiff does this feel like it could be merged to llm this week?
(trying to feel out the sequence relative to merge)
i think so, the most disruptive thing is probably /with going away. should we keep it?
pushed (to llm-multiobj)
If the alternative is better I don't have a problem with replacing it
Question @shrewd skiff : are slash commands generalizable somehow? How do we explain them vs. dot-builtins?
- Option 1: they're very different because: (reason that makes sense)
- Option 2: they're the same, move everything to slash commands
- Option 3: they're the same, move everything to dot builtins
at least at a technical level: dot builtins exist within the confines of valid shell syntax, slash commands don't; they're at a higher level of control, and / has to be the very first character in the prompt
i'm not sure - the UX is just different, it might be nice to not have to name something and just say "operate in this context"
So would slash commands make sense for anything that is applicable in both modes?
that might apply to some (not all) dot builtins
yeah
that might actually free up a lot of builtins
/login
/install
/uninstall
?
argh although install/uninstall is linked to filesystem navigation ("install in the current directory)
@shrewd skiff testing with latest llm-multiobj, vars dont seem to be accessible
Oh wait, I might have been using the wrong CLI build
(unrelated:) ah looks like we lost the stdout/stderr propagation, adding that back now
do you still want to add "ambient module access"?
if so, I have questions ๐
oh wait. token count works with openai now 
what's this again?
(not sure what to call it ๐
) the idea that /prompt and /shell have access to the same functions - including 1) core API, 2) functions in the current module, and 3) dependencies of the current module
cross-posting my multi-obj experiment: #p-agent-smith message
ack, it seems like this method of passing in self doesn't work very well with chaining - note how these all were called against the same initial empty toyWorkspace
ah, also lack of stderr pass-through tainted the result:
๐ง summarize what happened and what confused you
โ โ 0.0s
โ
โ๐ค I attempted to build a Docker container using a specific version of a git repository. Here's a summary of what happened:
โ โ
โ โ 1. Container Setup: I initiated a container with build tools like build-base , git , and later added go for building a Go-based CLI in
โ โ the project.
โ โ 2. Git Operations: I checked out the last stable release ( v0.16.2 ) of the repository.
โ โ 3. Build Attempts: I tried several times to build the main CLI using go build , targeting the ./cmd directory which typically houses
โ โ command entries. Each attempt resulted in a failure without substantial error details.
โ โ 4. Logging Issues: Attempts to capture build errors in log files ( stdout.log and stderr.log ) resulted in errors as the files weren't
โ โ created or found in the filesystem.
โ โ 5. Diagnosis: I examined directory contents and the go.mod file for clues but remained unable to identify the root cause of build failures.
โ โ
โ โ Confusion: My main confusion arose from:
โ โ
โ โ โข The inability to redirect and capture build error logs, which could have been instrumental in diagnosing the issue.
โ โ โข The absence of detailed error messages from the failed go build commands, leaving the exact reason for failure unclear.
โ โ
โ โ To proceed effectively, insights on specific build steps, environment setup, or access to documentation would be helpful.
โ โ 9.6s โ LLM Input Tokens: 22,229 โ LLM Output Tokens: 280
I'm still not convinced we can make this work without "simulating" mutation
pushed fix for that, it was goofy (needed to use errors.As, not directly cast)
yeah this self arg thingy was from back when it loaded all tools at once and didn't depend on switching contexts, lemme see if I can just undo it
pushed the removal of self but getting pretty bleh results https://dagger.cloud/dagger/traces/358ed43bfe65e8889f17577c8acc77aa
interested in this but no idea where to start
Will give it a try. Do you have a favorite scenario you run against it?
not really, I've just been winging it, it's actually the 'favorite scenarios' that I'm most interested in seeing haha
but here's one I've been using just to get things started:
go=$(container | from golang)
booklit=$(git https://github.com/vito/booklit | head | tree)
/prompt use $go to build $booklit
It's very vague but it's interesting seeing what it does. The general theory is "here's a container with Go, here's a trivially buildable Go project, do your thing" but even more ideally it would build ./cmd/booklit and give me the binary or something (nothing has done that yet, they just do go build ./... which is perfectly understandable given how vague the prompt is)
lots of variables could be tweaked, like having it figure out how to install Go itself
๐
@shrewd skiff I find myself entangling discussions of 1) dagger llm and dagger shell, and 2) multi-object. Should I embrace the entanglement and discuss it all in this thread? Or use a separate thread? (I want to involve @vernal ruin and discuss it live with you guys if possible... As the shell launch deadline looms large)
What about /model? Only specific to prompt mode...
i think this thread makes the most sense for now, since multi-obj feels like a major dependency of llm/shell merge (since it's what enables the magic var syncing)
eh, i could see wanting to run this while in shell mode just to get it out of the way, if you're in the middle of shell stuff and know you want to switch. but, if we want to, we can make the slash commands modal
I don't mind having /model there, I just want to make sure the overall architecture we have, makes sense to users, and scales nicely when we add more features. Ideally there are core principles that are self-evident and we don't start arguing over them in 6 months ๐
One interesting stress question: if /shell and /prompt are "modes", will there perhaps be more modes in the future? If so, what will be the rule for what belongs in a "mode"? Will they map to the Dagger API, for example 1 mode = 1 type? or something like that?
just to toss another option in the mix, Claude Code supports ! for running a shell command. typing it immediately does a UI cue to show the mode change
https://asciinema.org/a/cUB037ythDOmE8BaqKjUSN2Fw
so it's not really a mode switch in the persistent sense
which honestly is kind of nice, swapping back and forth is annoying
but it does raise the question of the 'default mode' (prompt, or shell? we're pretty sure about shell, but for claude it's prompt, so they use ! i guess to indicate 'danger, you're running a command'?)
if the default is shell, what do we use?
it's not too late to change the default ๐
i'm not proposing that, just saying ! prob won't make sense for a prompt indicator ๐
maybe it should match the symbol shown in the prompt, but for us that's a fancy unicode asterisk
regular asterisk aint too bad:
go=$(container | from golang)
repo=$(git https://github.com/vito/booklit | head | tree)
* build $repo with $go
gonna try implementing that and see how it feels
i'll do both sides, ! to run shell from prompt, * to run prompt from shell
i mean the alternative is typing /shell or /prompt no? and since it's interpreted immediately as a (one-off) mode switch it won't feel like you're literally typing "! foo"`
but yea i'll just take a swing at it and we can see how it feels
just tryin' things on
yeah - it doesn't show up in the text input, it swaps the mode immediately for that single input
here's a demo
I'm still not sure about slash-commands vs. dot-builtins
but I do like the claude hotkey for mode switch, now that I understand it
i think that'd imply running stuff on the host, not running dagger shell (I was confused by that in Claude too despite knowing it's not sandboxed)
But "dagger shell" will be the name of the whole thing (including prompt mode & all other modes). Basically it's the name of our repl.
It can either be that, or the name of a mode. Confusing if it's both
yeah but if you say "bash" people are gonna expect bash, which our scripting language isn't at all
true
also have to keep in mind dagger shell scripts which sort of exist outside of the idea of a repl
agree with this though
will we want to allow "prompt mode" in script files also? seems tempting!
yeah could be a flag 
dagger [shell] --prompt/-p <- in interactive mode, defaults to prompt, or when used in a shebang, interprets whole file as a prompt?
what what if I want to go back and forth in a script?
Maybe nobody will ever need that - but seems potentially risky to make that bet
but then, adding slash-commands to a shell script feels weird too
or maybe it's fine?
ok finally got the * key thing working - took a long detour so that it works nicely with history too (going back to a prompt history item swaps out to prompt mode, shell swaps to shell mode, etc)
https://asciinema.org/a/ERQSvuILuIH12RgDvS90H50Lm
TODO: swap the prompt back after you hit enter
oh nice! yeah I noticed that "history mixup"
by the way another bug log for later: if you start editing a new command while a previous command is running, your line gets wiped when the command completes (or fails?)
nice!
had agent write curl clone in Java. Then change it to use gradle instead of maven.
Got a request: "have you considered using a LLM to explain why my trace fails in Dagger Cloud? Could you do it better than Github Actions in their 'explain error' feature, because you have better data? Maybe even suggest actions to take?"
i have always looked at that button in github and never clicked it lol. have you?
makes sense, but sounds tricky (what AI do we use? who pays for it?)
Yeah those questions will have answers eventually ๐ But for now just a seed that needs water and time to grow
maybe this?
.prompt <<EOF
Do a thing.
EOF
i realize this resurfaces builtin vs slash commands, but I think slash commands never make sense in a script file
and maybe with this new model we don't need /prompt and /shell (MAYBE)
see my new video ๐
(not uploaded to yt yet)
@acoustic radish pushed if you want to try it out. llm still defaults to prompt mode, so press ! for shell, and if in shell mode press * for prompt
TODO for tomorrow:
- switch
shellto this new implementation - get rid of
llmcommand - update help text below prompt, current version is next to useless (M-? doesnt even work on Mac), should show
!/*key at minimum - add
-p/--prompttodagger shell?
nice nice!!
will try tomorrow tonight for sure
yeah in this model we may not need slash-commands (unless they replace dot-builtins ๐ but that's a stretch)
only one left would be /model?
There's still /compact /clear etc if we want em
I think it's a useful and common enough pattern to keep in our back pocket
ah right. those are cool
update help text below prompt, current version is next to useless (M-? doesnt even work on Mac), should show !/* key at minimum
pushed this part + fixed shell completion
@shrewd skiff anything I can to help on the "model UX" part? Ie. getting the LLM to better pick up the multi-object tools?
i think just a lot more tire-kicking - trying out more scenarios, seeing if it gets confused
i want to hack on script support to make that easier, like this:
#!/usr/bin/env -S dagger shell --no-mod
go=$(container | from golang)
repo=$(git https://github.com/vito/booklit)
bin=$(.prompt "Build the ./cmd/booklit binary in $repo using $go.")
$bin | terminal
but trying to resolve the merge situation with main first
btw @vernal ruin added -n I think
@shrewd skiff is shykes/dagger@llm-multiobj still the branch to build from?
yep
oops doesn't build atm, pushing fix + llm and main merge soon
OK I did a quick poll ( @devout niche @rough kiln @velvet garden ), we explored the question: "should dagger default to prompt or shell mode? And why?".
We reached consensus on:
- AI companies want software to be inside their AI. So their UI defaults to prompting.
- Dagger wants AI to be inside your software. So our UI defaults to shell.
And I really like that framing, because the UI difference actually reflects a profound philosophical difference.
cc @grizzled vine ๐ integration of marketing and UX ๐
makes sense to me 
Tibor mentioned "* is far on the keyboard, maybe >? Also it's a literal shell" ๐
@shrewd skiff re use of the word "shell".
I am warming up to:
- CLI: the entire command-line experience.
- shell mode: one way to interact with the CLI
- prompt mode: another way to interact with the CLI
FYI still doesn't build for me, just checking that it's normal
yep
@acoustic radish ok try now
done
@shrewd skiff assuming you're in the zone, going to try and fix that build error myself (seems unrelated to your code, maybe a main merge side effect?)
I'm out to dinner atm
--> moved to #1349529845436121112 since the issue doesn't seem specific to multiobj
ok now that shell is the default and we have one-off mode switching i think i'm team Remove Slash Commands. anything that was a slash-command can just be a builtin. and to run a builtin from prompt mode, just press ! first: instead of /compact it's !.compact.
also for a persistent mode switch maybe it can accept the switcher twice, so >> to stay in prompt mode, !! to stay in shell mode
do you think claude code users might miss the slash commands?
hmm i don't have a read on that really, need input from people who use it more. but, it has to be super discoverable for sure. as long as that's the case, and the UX isn't worse, I'd probably just go "oh" and move on.
so, need to try it and see if the ux feels worth it
i think this is closer to what we would have ended up with naturally, at least, since we already have a whole language with builtins and default to shell - claude code doesn't have that, and defaults to prompt
pushed: llm command gone, assimilated into shell, slash commands replaced with builtins.
currently working on: .prompt in repl swaps to prompt mode โ
, in a script it runs a prompt and returns the value
TODO: >>, !!, more polish
update: until the main build issue from hell is resolved ( #1349559353589497946 ) I a building llm-multiobj with dagger 0.6.3. Building now!
@shrewd skiff UX feedback: when I prompt the LLM, and it replies, it switches back to shell mode, so I often find myself typing my reply in the shell mode, getting an error; then have to fully re-type the same message because going back in history "switches" me to the shell mode
yeah that's why I want the persistent switching keybind
to fix the previous message, you can go to the beginning and type > after-the-fact
Yeah kind of feels like there should be a "regular" keybind (say ctrl-/) that can be triggered at any time (not just when editor is empty) and its only effect is to toggle the input mode (only visual effect is to change the prompt character). Everything else remains the same including my current line buffer.
the >/! prefix also works when the editor has content, you just have to be at position=0 when you type it - maybe try that out? it's the same way Claude Code works and feels intuitive once you know it's a thing (Up-> Home/Ctrl-A -> >)
keybind makes sense too but a) it's especially difficult to find one that works across all platforms in prompt mode, and b) it'll compete with >/! for discoverability - e.g. which do we show in the keymap?
If we do a keybind it would be as a replacement for > > !, I wouldn't do both. Will try the "position 0" trick
@shrewd skiff OK with stream of consciousness feedback as it comes to me while using? Or do you prefer a thoughtful, digested summary at the end of the day (crazy I know)
all good!
lots of stuff is changing so it's good to check in and build muscle memory on things like that sooner
@shrewd skiff I'm getting the hang of it now. But would prefer a toggle mechanic rather than having to memorize two different characters I think. I can see how two different characters is more idempotent though, you can mash that key 10 times and know exactly the end state
(but current behavior of having to carefully orchestrate the cursor position before pressing those two characters, is NOT idempotent)
will think on it, but want to give a bit of time for muscle memory still; the mental model is that there's an invisible symbol at the start of the input, for example pressing backspace at position=0 undoes the mode change too. IME so far it's pretty smooth, so maybe familiarity with the old way (persisten mode switching) is still getting in the way
you might be dogfooding it even more than me at this point though

we are so back
backspace undoing the mod change bit me a few times. I tend to compulsively press backspace more than necessary, and it dropping down to shell is disruptive
But agree with giving it a little time (just not too much time ๐ )
My nickname for this model is "russian doll": there is shell mode, and then inside it there is prompt mode. There is a clear hierarchy
ah yeah that's fair
i'm a Ctrl+Wer
Bug report: when switching to prompt mode, with a 1password secret reference for llm config:
- I press
> - I get the 1password popup
- Almost immediately, this error appears:
context deadline exceeded - Pressing
>again works
oh fun
will fix, it's from the lazy initialization, tricky to work in to the UI loop
there's a 1 second timeout there which i thought would never get hit, forgot about password prompts ๐
yeah and don't forget - user consent prompts are coming from @knotty imp too
๐ญ
latest multi-obj CLI & engine ๐
goddamn it ๐
Then:
โ base_container=$(container | from alpine) 1.2s
โ โ container: Container! 0.0s
โ โ .from(address: "alpine"): Container! 1.1s
! returned error 422: {"data":null,"errors":[{"message":"Cannot query field \"loadLlmFromID\" on type \"Query\
โ```
So, same error as before @shrewd skiff . I don't think it's a version mismatch issue
btw it's a little addictive that I can build + run any version of dagger, bootstrapped from an older version of dagger, with no git checkout or other dependencies, in another command
as our build gets gradually faster, this flow becomes more and more like a superpower
@acoustic radish for backwards compatibility I brought back the withFoo/foo setters/getters. worth it?
pushed - added the multi-object feature flag but kept its default as true for now since we're still on the multiobj branch
you mean as a temporary stopgap to make merging + feature flag easier?
or a more permanent reversal?
just wondering how concerned we are with keeping existing LLM demos working after merging llm-multiobj ultimately into main
without those, they'll have to make up a variable name i suppose
I feel like it's OK to break everything that came before merging into main
as long as it feels like the best version of the API so far, at the time of merge
i'm not sure tbh - there's something about the single-object API that's still compelling
i don't have strong feelings on it yet though
re: mode switching, now that I'm using it, maybe the mode switch should just be persistent
I got the same feeling by watching @acoustic radish use it
Yeah I got it wrong like 10 times in one demo
lol
I managed to get it wrong once in the other direction - gave a shell command to the llm, and it actually executed it for me (for a small fee)
pushed persistent modes
pushed a fix for var are autocompletion in prompt mode (that's a thing)
pushed some more UI polish
- builtin tool calls now look like function calls
- actor emoji is now respected on non-message spans
- syntax highlighting
could do a custom "tool call" UI if we're worried about overloading the regular function call look
update: I merged into llm, so maybe time to retire this thread
I'm not 100% sure we actually need one - currently it's on by default, but I'm thinking of changing it to just turn on once you set a variable. Less fiddly
I merged a bit aggressively because I'm tired of conflicts ๐ - going to just keep working off of llm since the plan was already made to work forward from llm-multiobj
we might want an explicit opt-in for shell mode though, depends on what we want to demo from here on
since setting a variable in shell is obviously going to be common
If there's no FF then we need to prepare for breaking agent modules
they shouldn't break - they'll just keep using the withFoo/foo APIs
ah so for now both APIs still coexist ?
yep
the LLM type is a chonker at the moment, though it probably already crossed that threshold haha
i think there's a chance this is actually a desired state anyway
since the notion of a "current state" never really went away
and not having to name things when you don't need to is nice (for the programmatic API)
but, it's up in the air
I worry that it will be too much to grok...
but it was the right call to leave for now
we still have things to add before we start slimming it down:
1- modules
2- access to core API?
3- access to current module's functions and dependencies
no idea how to do 2 and 3
for 2) do you mean conceptually swapping the tools to Query?
(maybe without all the loadFooFromIDs)
Not sure. Thinking about prompt mode and how to give your "copilot" access to the same environment as the shell (if you want it to)
or, maybe we explicitly don't want to
same for modules
one issue at the moment with passing modules: agent can never call the constructor
@shrewd skiff mind if we talk live about this sometime today?
sure
was talking to Guillaume and Tibor about how MCP plugs into multiobj, and ๐คทโโ๏ธ
I wanna come too ๐
all are welcome ๐ on a call now but will propose a time after. maybe tell me what times work for you today?
I'll be good in about an hour - grabbing lunch
@knotty imp @acoustic radish I'm good whenever, no rush, just checking in in case I'm the bottleneck
ready in 5mn
i am ready whenever, backfilling tests is like the most interruptible thing ever lol
I wish I could thread a thread ๐ฌ what's the substitute for WithPromptVar if I'm converting some code?
SetString
forgot about that one - could add it back, since I brought back the withFoo ones, it'd be a drop in the bucket
no worries, I found the SetFoo too so I'm fully immersing myself ๐
I've been thinking about bind as a possible verb.
I find myself using the terminology "bind an object to the llm environment" and it seems to work (ie. people understand & like it ๐
I like it more than set which sounds like a mutation
do you have a new one for get? ๐
fwiw I still find with the clearest, aside from the clash with the current non-variable version
(which would be a dealbreaker if we're keeping it, unless we make name optional, but that would clutter up code usage)
give and take? ๐ (though 'take' sounds like the LLM no longer has it lol)
yeet and yoink
if set is bind, get could ostensibly be bound
need a new plan
in the FP bind analogy i don't think there's a named operator to get the value of a bound variable, but when talking about it you do call the thing a bound variable lol
ok joining dev-audio!
(cc @knotty imp if you wanted to join)
another option for the pile:
withContainerVar("foo", ctr)
containerVar("foo")
(aligns with withPromptVar but I guess that could also be withStringVar)
haha makes me think of withServiceBinding() ๐
self-bump, i'd like feedback on this not because i think it's necessarily a good idea, but because i wanna understand if i have any clue what's going on in this API design convo XD
Didn't see that, yeah makes sense to me ๐ so like boundContainer
usually verb tenses as APIs are a smell, but if bind is the right word it does kinda work
withServiceBinding becomes kind of an awkward inconsistency though (different use of unique word, different tense)
Possible pairs:
with<Foo>Binding+get<Foo>Bindingwith<Foo>Binding+<Foo>BindingbindFoo+getFoobindFoo+boundFoo
usually this isn't an issue, not sure