X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/d9fddc0f506eb4ead7049f5638674b2b8773fcaa..496569ab5b179932c60b2924bf4c61176c8be004:/chain.go?ds=inline diff --git a/chain.go b/chain.go index 0211787..f201812 100644 --- a/chain.go +++ b/chain.go @@ -22,21 +22,36 @@ type ChainClient interface { // answer. The replies, the tool calls, and the results go to the log of tools // as they happen. The thoughts never do. // -// Each later request begins with the whole input of the request before it, -// then its output items, then the tool results. The output items hold the -// reasoning with its encrypted content, because the calls do not store it. So -// each request begins with all of the previous one and reads it from the prompt +// Each later request begins with the whole input of the request before it, then +// its output items, then the tool results. The output items hold the reasoning +// with its encrypted content, because the calls do not store it. So each +// request begins with all of the previous one and reads it from the prompt // cache. The messages of take join each later request after the tool results. type Chain struct { client ChainClient tools *Toolbox request ModelRequest take func() []Message + images *ImageStore +} + +// ChainFunctionCall holds the fields of a function call output item. +type ChainFunctionCall struct { + Type string `json:"type"` + Name string `json:"name"` + Arguments string `json:"arguments"` + CallID string `json:"call_id"` +} + +// ChainFunctionCallOutput is the input item with the result of a function call. +type ChainFunctionCallOutput struct { + Type string `json:"type"` + CallID string `json:"call_id"` + Output any `json:"output"` } // Run runs the chain from input, the input of its first request, and returns -// the text of the final answer. An error of a log write wraps -// errChainLogWrite. +// the text of the final answer. An error of a log write wraps errChainLogWrite. func (c *Chain) Run(ctx context.Context, input []any) (string, error) { request := c.request for request.Turn = "first"; ; request.Turn = "next" { @@ -47,6 +62,7 @@ func (c *Chain) Run(ctx context.Context, input []any) (string, error) { } var text string + input, text, err = c.handle(ctx, input, response) if err != nil { return "", err @@ -59,12 +75,15 @@ func (c *Chain) Run(ctx context.Context, input []any) (string, error) { } // handle logs the replies of response and runs its tool calls. It returns the -// input of the next request: input, the output items, the tool results, and -// the messages of take. After the final answer it returns no input, and the -// replies of the answer as its text. +// input of the next request: input, the output items, the tool results, and the +// messages of take. After the final answer it returns no input, and the replies +// of the answer as its text. func (c *Chain) handle(ctx context.Context, input []any, response ModelResponse) ([]any, string, error) { - var outputs []any - var replies []string + var ( + outputs []any + replies []string + ) + for _, raw := range response.Output { item, err := chainInputItem(raw) if err != nil { @@ -74,6 +93,7 @@ func (c *Chain) handle(ctx context.Context, input []any, response ModelResponse) input = append(input, item) var call ChainFunctionCall + err = json.Unmarshal(raw, &call) if err != nil { return nil, "", fmt.Errorf("decode output item: %w", err) @@ -117,33 +137,23 @@ func (c *Chain) handle(ctx context.Context, input []any, response ModelResponse) input = append(input, outputs...) for _, message := range c.take() { - input = append(input, modelUserMessage(message.Text)) + item, err := imageUserMessage(c.images, message.Text) + if err != nil { + return nil, "", fmt.Errorf("read queued images: %w", err) + } + + input = append(input, item) } return input, "", nil } -// ChainFunctionCall holds the fields of a function call output item. -type ChainFunctionCall struct { - Type string `json:"type"` - Name string `json:"name"` - Arguments string `json:"arguments"` - CallID string `json:"call_id"` -} - -// ChainFunctionCallOutput is the input item with the result of a function -// call. -type ChainFunctionCallOutput struct { - Type string `json:"type"` - CallID string `json:"call_id"` - Output string `json:"output"` -} - // chainInputItem returns an output item as an input item for the next request. // The calls do not store their items, so the item goes back whole, without its // id. func chainInputItem(raw json.RawMessage) (map[string]any, error) { var item map[string]any + err := json.Unmarshal(raw, &item) if err != nil { return nil, fmt.Errorf("decode output item: %w", err)