Implement Canvas V2 subgraphs, assets and asynchronous AI execution
This commit is contained in:
@@ -22,10 +22,15 @@ var errOutputLimit = errors.New("Codex output limit exceeded")
|
||||
type outputBuffer struct {
|
||||
buffer bytes.Buffer
|
||||
exceeded bool
|
||||
limit int
|
||||
}
|
||||
|
||||
func (b *outputBuffer) Write(p []byte) (int, error) {
|
||||
if len(p) > maxOutput-b.buffer.Len() {
|
||||
limit := b.limit
|
||||
if limit == 0 {
|
||||
limit = maxOutput
|
||||
}
|
||||
if len(p) > limit-b.buffer.Len() {
|
||||
b.exceeded = true
|
||||
return 0, errOutputLimit
|
||||
}
|
||||
@@ -61,17 +66,63 @@ func ServeWorker(socket string) error {
|
||||
execute(w, r, input.Prompt)
|
||||
}
|
||||
})
|
||||
server := &http.Server{Handler: mux, ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 10 * time.Second, WriteTimeout: timeout + 15*time.Second}
|
||||
mux.HandleFunc("GET /canvas/models", func(w http.ResponseWriter, r *http.Request) {
|
||||
models, err := workerCanvasModels()
|
||||
if err != nil {
|
||||
fail(w, 503, "codex_unavailable")
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"models": models})
|
||||
})
|
||||
mux.HandleFunc("POST /canvas/exec", func(w http.ResponseWriter, r *http.Request) {
|
||||
var input CanvasTask
|
||||
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, canvasArtifactResponseLimit+CanvasInputLimit))
|
||||
decoder.DisallowUnknownFields()
|
||||
if decoder.Decode(&input) != nil || decoder.Decode(new(any)) != io.EOF || len(input.Prompt) > CanvasInputLimit || strings.TrimSpace(input.Prompt) == "" || len(input.Parameters) > 0 || len(input.Images) > 4 {
|
||||
fail(w, 400, "invalid_ai_request")
|
||||
return
|
||||
}
|
||||
models, err := workerCanvasModels()
|
||||
if err != nil {
|
||||
fail(w, 503, "codex_unavailable")
|
||||
return
|
||||
}
|
||||
var selected *CanvasModel
|
||||
for _, model := range models {
|
||||
if model.ID == input.ModelID {
|
||||
copy := model
|
||||
selected = ©
|
||||
}
|
||||
}
|
||||
if selected == nil {
|
||||
fail(w, 422, "model_unavailable")
|
||||
return
|
||||
}
|
||||
select {
|
||||
case slot <- struct{}{}:
|
||||
defer func() { <-slot }()
|
||||
default:
|
||||
w.Header().Set("Retry-After", "5")
|
||||
fail(w, 429, "codex_busy")
|
||||
return
|
||||
}
|
||||
executeCanvasTask(w, r, input, *selected)
|
||||
})
|
||||
server := &http.Server{Handler: mux, ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 10 * time.Second, WriteTimeout: 195 * time.Second}
|
||||
return server.Serve(listener)
|
||||
}
|
||||
|
||||
func execute(w http.ResponseWriter, r *http.Request, prompt string) {
|
||||
func execute(w http.ResponseWriter, r *http.Request, prompt string) { executeModel(w, r, prompt, "") }
|
||||
func executeModel(w http.ResponseWriter, r *http.Request, prompt, model string) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, executable, "--ask-for-approval", "never",
|
||||
"--disable", "shell_tool", "--disable", "apps", "--disable", "plugins",
|
||||
"--disable", "multi_agent", "--disable", "hooks", "--disable", "memories",
|
||||
"-c", `web_search="disabled"`, "exec", "--json", "--sandbox", "read-only", "--skip-git-repo-check", "--ephemeral", "-")
|
||||
if model != "" {
|
||||
cmd.Args = append(cmd.Args[:len(cmd.Args)-1], "--model", model, "-")
|
||||
}
|
||||
cmd.Dir = "/work"
|
||||
cmd.Env = []string{"PATH=/usr/local/bin:/usr/bin:/bin", "HOME=/home/node", "CODEX_HOME=/home/node/.codex", "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"}
|
||||
cmd.Stdin = strings.NewReader(prompt)
|
||||
|
||||
Reference in New Issue
Block a user