blob: 250919f60d8c935721b261d6802558205c05fe37 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package codeimage extracts Go code from images.
package codeimage
import (
"context"
"errors"
"fmt"
"go/format"
"io"
"net/http"
"strconv"
"golang.org/x/oscar/internal/llm"
)
// InURL looks for code in the image referred to by url.
// It fetches the url using the given HTTP client, then calls [InBlob].
func InURL(ctx context.Context, url string, client *http.Client, cgen llm.ContentGenerator) (string, error) {
res, err := client.Get(url)
if err != nil {
return "", err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return "", fmt.Errorf("%s returned status %s", url, res.Status)
}
data, err := io.ReadAll(res.Body)
if err != nil {
return "", err
}
return InBlob(ctx, llm.Blob{
MIMEType: res.Header.Get("Content-Type"),
Data: data,
}, cgen)
}
// InBlob looks for code in the given blob.
// On success, it returns a properly formatted Go program or program fragment.
func InBlob(ctx context.Context, blob llm.Blob, cgen llm.ContentGenerator) (string, error) {
parts := []llm.Part{
llm.Text(instructions),
blob,
}
schema := &llm.Schema{
Type: llm.TypeString,
Description: "the program",
}
// Retry several times in the hope that the LLM will eventually produce a valid program.
for try := 0; try < 3; try++ {
output, err := cgen.GenerateContent(ctx, schema, parts)
if err != nil {
return "", err
}
unq, err := strconv.Unquote(output)
if err != nil {
return "", fmt.Errorf("strconv.Unquote: %w", err)
}
fbytes, err := format.Source([]byte(unq))
if err != nil {
// Retry if it isn't a valid program.
continue
}
return string(fbytes), nil
}
return "", errors.New("could not produce a valid Go program")
}
const instructions = `
The following image contains code in the Go programming language.
Extract the Go code from the image.
Make sure you produce a syntactically valid Go program.
`