ListTemplates returns a tool that lists available workspace templates. The agent uses this to discover templates before creating a workspace. Results are ordered by number of active developers (most popular first) and paginated at 10 per page. db must not be nil.
(db database.Store, organizationID uuid.UUID, options ListTemplatesOptions)
| 37 | // and paginated at 10 per page. |
| 38 | // db must not be nil. |
| 39 | func ListTemplates(db database.Store, organizationID uuid.UUID, options ListTemplatesOptions) fantasy.AgentTool { |
| 40 | return fantasy.NewAgentTool( |
| 41 | "list_templates", |
| 42 | "List available workspace templates. Optionally filter by a "+ |
| 43 | "search query matching template name or description. "+ |
| 44 | "Use this to find a template before creating a workspace. "+ |
| 45 | "Results are ordered by number of active developers (most popular first). "+ |
| 46 | "Returns 10 per page. Use the page parameter to paginate through results.", |
| 47 | func(ctx context.Context, args listTemplatesArgs, _ fantasy.ToolCall) (fantasy.ToolResponse, error) { |
| 48 | ctx, err := asOwner(ctx, db, options.OwnerID) |
| 49 | if err != nil { |
| 50 | return fantasy.NewTextErrorResponse(err.Error()), nil |
| 51 | } |
| 52 | |
| 53 | filterParams := database.GetTemplatesWithFilterParams{ |
| 54 | Deleted: false, |
| 55 | OrganizationID: organizationID, |
| 56 | Deprecated: sql.NullBool{ |
| 57 | Bool: false, |
| 58 | Valid: true, |
| 59 | }, |
| 60 | } |
| 61 | query := strings.TrimSpace(args.Query) |
| 62 | if query != "" { |
| 63 | filterParams.FuzzyName = query |
| 64 | } |
| 65 | |
| 66 | var allowlist map[uuid.UUID]bool |
| 67 | if options.AllowedTemplateIDs != nil { |
| 68 | allowlist = options.AllowedTemplateIDs() |
| 69 | } |
| 70 | if len(allowlist) > 0 { |
| 71 | filterParams.IDs = slices.Collect(maps.Keys(allowlist)) |
| 72 | } |
| 73 | templates, err := db.GetTemplatesWithFilter(ctx, filterParams) |
| 74 | if err != nil { |
| 75 | return fantasy.NewTextErrorResponse(err.Error()), nil |
| 76 | } |
| 77 | |
| 78 | // Look up active developer counts so we can sort by popularity. |
| 79 | templateIDs := make([]uuid.UUID, len(templates)) |
| 80 | for i, t := range templates { |
| 81 | templateIDs[i] = t.ID |
| 82 | } |
| 83 | ownerCounts := make(map[uuid.UUID]int64) |
| 84 | if len(templateIDs) > 0 { |
| 85 | rows, countErr := db.GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx, templateIDs) |
| 86 | |
| 87 | if countErr == nil { |
| 88 | for _, row := range rows { |
| 89 | ownerCounts[row.TemplateID] = row.UniqueOwnersSum |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // Sort by active developer count descending. |
| 95 | slices.SortStableFunc(templates, func(a, b database.Template) int { |
| 96 | return cmp.Compare(ownerCounts[b.ID], ownerCounts[a.ID]) |