X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/eb05edcf5d575e8a3b1040debfb57423312e8320..80e348724041a694002fc38f57e8868b9b9a2be9:/chain.go diff --git a/chain.go b/chain.go index 3ab9fe1..59ce563 100644 --- a/chain.go +++ b/chain.go @@ -32,6 +32,23 @@ type Chain struct { 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 @@ -43,10 +60,11 @@ func (c *Chain) Run(ctx context.Context, input []any) (string, error) { request.Input = input response, err := c.client.Create(ctx, request) if err != nil { - return "", err + return "", fmt.Errorf("create model response: %w", err) } var text string + input, text, err = c.handle(ctx, input, response) if err != nil { return "", err @@ -63,8 +81,11 @@ func (c *Chain) Run(ctx context.Context, input []any) (string, error) { // 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 +95,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 +139,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)