Appearance
Go walkthrough
Upload a document, wait for it to be ready, then ask a question and print the answer with its source files.
Before you start
Go 1.22 or later. The example uses only the standard library. Create a Read & write API key in the console.
Set these environment variables. Keep the API key private.
bash
export LAB0_API_KEY='lab0_sk_replace_with_your_key'
export LAB0_BASE_URL='https://app.lab0.ai/app'
export LAB0_NAMESPACE='quickstart'Save a document
Save the following as plan.md, or download it:
md
# Plan
The rollout uses quillfeather checklists.The example uploads this file by default. Set LAB0_FILE_PATH to use another path, and change the prompt to match its contents.
Upload, wait and ask
Save this as quickstart.go, or download the example.
go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
func value(name, fallback string) string {
if v := os.Getenv(name); v != "" {
return v
}
return fallback
}
func run() error {
key := os.Getenv("LAB0_API_KEY")
if key == "" {
return fmt.Errorf("set LAB0_API_KEY to a Read & write key")
}
base := strings.TrimRight(value("LAB0_BASE_URL", "https://app.lab0.ai/app"), "/")
namespace := value("LAB0_NAMESPACE", "quickstart")
client := &http.Client{Timeout: 200 * time.Second}
request := func(method, route, contentType string, body io.Reader) (map[string]any, error) {
req, err := http.NewRequest(method, base+route, body)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
text, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, text)
}
var result map[string]any
err = json.NewDecoder(resp.Body).Decode(&result)
return result, err
}
path := value("LAB0_FILE_PATH", "plan.md")
document, err := os.Open(path)
if err != nil {
return err
}
defer document.Close()
var upload bytes.Buffer
form := multipart.NewWriter(&upload)
part, err := form.CreateFormFile("file", filepath.Base(path))
if err != nil {
return err
}
if _, err = io.Copy(part, document); err != nil {
return err
}
if err = form.WriteField("namespace", namespace); err != nil {
return err
}
if err = form.Close(); err != nil {
return err
}
uploaded, err := request("POST", "/api/knowledge/files", form.FormDataContentType(), &upload)
if err != nil {
return err
}
fileID, ok := uploaded["id"].(string)
if !ok {
return fmt.Errorf("upload response has no file id")
}
ready := false
for attempt := 0; attempt < 90; attempt++ {
file, err := request("GET", "/api/knowledge/files/"+fileID, "", nil)
if err != nil {
return err
}
if file["status"] == "ready" {
ready = true
break
}
if file["status"] == "failed" {
return fmt.Errorf("processing failed: %v", file["error"])
}
time.Sleep(2 * time.Second)
}
if !ready {
return fmt.Errorf("processing timed out; inspect file %s", fileID)
}
payload, err := json.Marshal(map[string]any{
"prompt": "How is the rollout run?", "namespace": namespace, "file_ids": []string{fileID},
})
if err != nil {
return err
}
answer, err := request("POST", "/api/knowledge/respond", "application/json", bytes.NewReader(payload))
if err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(answer)
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}Run it from the directory containing plan.md:
bash
go run quickstart.goThe example stops on an HTTP error, a failed upload or a processing timeout. Once the file is ready, it asks “How is the rollout run?” using only the uploaded file.
Read the result
A successful response contains answer, sources and model. The document says that the rollout uses quillfeather checklists; verify that the answer reflects that and that sources identifies plan.md. Exact wording and the model name can vary.
The file stays in your namespace for later questions. See cleanup when you finish, or continue with streaming answers and the API reference.