Add support for Deferred Functions #156
                
     Draft
            
            
          
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Deferred Function Calling
(Found out later that Pydantic AI also has a feature like this - https://ai.pydantic.dev/deferred-tools/)
Motivation
Normally, when a function call happens in an AI system, it looks like this:
sequenceDiagram participant CP as Chat Prompt participant LLM participant Fn as Function CP ->> LLM: Messages, system prompt<br/>function metadatas LLM ->> CP: Call Fn1 with arguments X CP ->> Fn: execute(X) Fn ->> CP: result (string) CP ->> LLM: Messages + Fn ResultThis works for many use-cases. In this example, we're assuming F1 is some function that can be called and resolved in a reasonable amount of time (maybe a few seconds).
However, there are some scenarios where the function cannot be easily (or quickly) be executed. Examples include:
Proposal
In this PR, I'm proposing the ability for functions to return a DeferredResult in addition to a string. The idea is that if the ChatPrompt sees "DeferredResult", it saves that state, and stops executing.
Then, when the system is ready (aka gets an external signal) to continue, then it "resumes" and continues the ChatPrompt. The updated sequence diagram for a Deferrable Function looks like so:
sequenceDiagram participant Teams participant CP as Chat Prompt participant LLM participant Fn as Deferrable Function Teams ->> CP: send("buy some stock") (via Message handler) CP ->> LLM: Messages, system prompt<br/>function metadatas LLM ->> CP: Call Fn1 with arguments X CP ->> Fn: execute(X) Fn ->> Teams: Ask for approval Fn ->> CP: DeferredResult(state) CP ->> Teams: No response break When signal is received Teams ->> CP: "yes" go ahead<br/>resume(input) CP ->> Fn: Can this resume using input? Fn ->> CP: true CP ->> Fn: Resume(input) Fn ->> CP: Result after resuming (string)<br/>"the user approved the buying of the stock" end CP ->> LLM: Messages + Fn ResultImplementation
Messagewhich is a union of different types of messages). ThisDeferredMessagebasically stores the deferred state that the executable function can optionally return.DeferredResultnow (on top of string)DeferredMessagewith aFunctionMessagewhich is a normal function call result.Questions
MessagenotActivity. Thoughts?