#loop() 🧵
1 messages · Page 1 of 1 (latest)
@cosmic orchid you mentioned you had an idea to maybe bring loop back? Tell me more 🙂
Oh, yeah I think it's still useful. Well, kind of. It at least does something.
Take this example:
return dag.Llm(dagger.LlmOpts{Model: m.Model}).
SetContainer("ctr", dag.Container()).
WithPrompt("set up $ctr for PHP 7 development").
Loop().
WithPrompt("now install nano").
Loop().
WithPrompt("undo that and install vim instead").
Loop().
GetContainer("ctr")
If you remove the Loop() calls it just sends all of the prompts at once, instead of doing them in sequence, so the model sees everything and behaves differently than if you wait after each one.
I'm not sure if you'd want this for non-test code though. And this could be using Sync() instead but then I'd have to break the chain and make the code ugly.
But isn't Sync() the same thing?
Or you're saying, Loop() doesn't unlazily. It lazily requests an agentic loop to be done at that point in the chain
yeah exactly, Sync() is Loop() but designed for our unlazying pattern, this allows you to prescribe the looping points ahead of time without having to unlazy at the call site (and change your function signature, etc.)
But, how does that play with GetContainer() in your example?
but i don't feel too strongly about this, like arguably the above code should actually Sync() and somehow assert what it expects from the reply or the state
Currently GetContainer() forces sync. Will it also force loop?
because right now when it messes up it just moves on to the next prompt like nothing happens lol
The current behavior is that this will work:
return dag.LLM().SetContainer().WithPrompt().GetContainer()
Also identical behavior to:
return dag.LLM().WithPrompt().SetContainer().GetContainer()
without the Loops, GetContainer will force the sync, but the model will receive all 3 prompts at once, which totally changes how they're interpreted
instead of "do this" <loop> "then that" <loop> "then that" <loop> it turns into "do all these things, which somewhat contradict each other and dont make sense anymore" <loop>
I see. Already possible with Sync() but like you said, less practical because of error checking
Should we remove sync() completely? and replace it with loop?
no Sync is still useful because you still need that 'returns a scalar ID that gets rehydrated into an Object' trick to force something to actually happen
and the CLI automatically calls it (like all other Syncers)
Ah right
note that I wasn't able to use the real Syncer mechanism, because its implementation is tied to llb state
so it's a handrolled Sync() which has its own weird relationship to laziness
Ie. at the moment if I call LLM.Sync() directly, I can/must error check
But if I call LLM.GetContainer() (which calls sync()` I don't
I actually have no idea what happens if the underlying container binding is still lazy, then I call GetContainer(), it gets unlazied by nested sync(), and returns an error. What happens to my error-less GetContainer() outer call?
it's the same principle as everywhere else in the API really; you can do withExec(["boom"]).file("not-created"), mount that into a container, and you won't see the error until that outer container is unlazied
yeah this was easy enough to work around, we should probably just generalize the interface a bit more but in the end it's just internal syntax sugar
Can I suggest Turn instead of Loop?
I believe the term "turn" is more commonly used for LLMs.
I think "turns" are usually used for AI dialogues/conversations
But I did see "turn" but didn't ever see "loop" in the context of LLMs