59 lines
2.5 KiB
Go
59 lines
2.5 KiB
Go
package platform
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
func (s *service) canvasCapabilities(w http.ResponseWriter, r *http.Request, project, account string) error {
|
|
if r.Method != http.MethodGet {
|
|
reject(w, 405, "method_not_allowed")
|
|
return nil
|
|
}
|
|
tx, err := s.db.BeginTx(r.Context(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
kind, _, err := lockMaterial(r.Context(), tx, project, r.PathValue("workspace"), r.PathValue("material"), account)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if kind != "board" {
|
|
reject(w, 400, "invalid_material")
|
|
return nil
|
|
}
|
|
if err = tx.Rollback(); err != nil {
|
|
return err
|
|
}
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
defer cancel()
|
|
models, modelErr := s.canvasAI.Models(ctx)
|
|
textAvailable, imageAvailable := false, false
|
|
if modelErr == nil {
|
|
for _, model := range models {
|
|
if model.Available {
|
|
textAvailable = textAvailable || canvasModelSupports(model, "text")
|
|
imageAvailable = imageAvailable || canvasModelSupports(model, "image")
|
|
}
|
|
}
|
|
}
|
|
ai := response{"available": textAvailable || imageAvailable, "text": textAvailable, "image": imageAvailable}
|
|
if modelErr != nil {
|
|
ai["unavailable_reason"] = "codex_unavailable"
|
|
}
|
|
reply(w, 200, response{
|
|
"contract_version": "canvas-headless-2026-11-07",
|
|
"canonical_model": response{"document_format_version": canvasModelFormat, "protocol_version": canvasModelProtocol, "explicit_migration": true, "stable_element_ids": true, "atomic_multi_canvas": true, "logical_operation_dedup": true, "legacy_snapshot_writes": false},
|
|
"supported_schema_versions": []int{1, 2},
|
|
"edge_features": response{"appearance": true, "markers": true, "bezier_chain": true, "animation_configuration": true, "execution_events": true},
|
|
"subgraphs": response{"create": true, "migrate": true, "undo": true, "members": true, "dissolve": true, "connections": true, "member_positions": true},
|
|
"ai": ai,
|
|
"assets": response{"previews": true, "preview_max_edge": 1024, "originals_preserved": true},
|
|
"limits": response{"max_depth": 8, "max_nodes": 500, "max_edges": 2000, "max_frame_ports": 4000, "max_curve_knots": 64, "max_snapshot_bytes": canvasLimit, "max_active_tasks": 128, "max_run_steps": 64, "max_execution_depth": 32, "max_input_images": 4, "max_input_image_bytes": 20 << 20, "max_coordinate": 1000000},
|
|
"retention": response{"idempotency_min_seconds": 86400, "undo_receipt_seconds": 604800},
|
|
})
|
|
return nil
|
|
}
|