var errModelCallLimit = errors.New("call limit reached")
// modelEndpoint returns the URL and the authorizer of the endpoint in config.
-// The ChatGPT backend uses the token file in dir. The OpenAI API uses the key in
-// OPENAI_API_KEY, and there is no fallback from one to the other. All clients
-// share the authorizer, so one refresh runs at a time.
+// The ChatGPT backend uses the token file in dir. The OpenAI API uses the key
+// in OPENAI_API_KEY, and there is no fallback from one to the other. All
+// clients share the authorizer, so one refresh runs at a time.
func modelEndpoint(config Config, dir string) (string, ModelAuthorizer, error) {
if config.Endpoint == configEndpointAPI {
key := os.Getenv("OPENAI_API_KEY")
// modelUserMessage returns a user input item with one text part for each text.
func modelUserMessage(texts ...string) any {
- type part struct {
- Type string `json:"type"`
- Text string `json:"text"`
- }
-
- content := make([]part, len(texts))
+ content := make([]ModelInputPart, len(texts))
for n, text := range texts {
- content[n] = part{
+ content[n] = ModelInputPart{
Type: "input_text",
Text: text,
}
}
+ return modelUserParts(content)
+}
+
+// modelUserParts keeps text and image parts in one user message.
+func modelUserParts(content []ModelInputPart) any {
return struct {
- Role string `json:"role"`
- Content []part `json:"content"`
+ Role string `json:"role"`
+ Content []ModelInputPart `json:"content"`
}{
Role: "user",
Content: content,
} `json:"content"`
}
+// ModelInputPart is a text or image part in a user message or tool result.
+type ModelInputPart struct {
+ Type string `json:"type"`
+ Text string `json:"text,omitempty"`
+ ImageURL string `json:"image_url,omitempty"`
+}
+
// ModelRequest is one model call. Input holds the input items in order, such as
// modelUserMessage values or output items of an earlier response. Node names
// the node that a compaction builds, or is nil. Turn names the request of a