codersdk/skills.go
Rama main · réplica de lectura del repositorio original.
package codersdk
import (
"context"
"fmt"
"net/http"
"net/url"
"time"
"github.com/google/uuid"
)
// SkillMetadata represents a personal or organization skill without its raw
// Markdown content.
type SkillMetadata struct {
ID uuid.UUID `json:"id" format:"uuid"`
Name string `json:"name"`
Description string `json:"description"`
Enabled bool `json:"enabled"`
// Action reports whether a project skill is offered as a one-click
// action above project composers. It is always false for personal and
// organization skills.
Action bool `json:"action"`
// ModelInvocable reports whether agents can discover and load the skill
// on their own. Users can still invoke a skill explicitly when it is
// false.
ModelInvocable bool `json:"model_invocable"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
}
// Skill represents a personal or organization skill with its raw Markdown
// content.
type Skill struct {
SkillMetadata
Content string `json:"content"`
}
// CreateSkillRequest is the payload for creating a skill.
type CreateSkillRequest struct {
// Content must be SKILL.md-format Markdown with YAML frontmatter. The
// frontmatter must include name, may include description, and must be
// followed by a non-empty body.
Content string `json:"content"`
// Action is only accepted for project skills and defaults to false.
Action *bool `json:"action,omitempty"`
// ModelInvocable defaults to false when the content's frontmatter sets
// disable-model-invocation: true, and to true otherwise.
ModelInvocable *bool `json:"model_invocable,omitempty"`
}
// UpdateSkillRequest is the payload for updating a skill. At least one field
// must be set.
type UpdateSkillRequest struct {
// Content must be SKILL.md-format Markdown with YAML frontmatter. The
// frontmatter must include name, may include description, and must be
// followed by a non-empty body.
Content *string `json:"content,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
// Action is only accepted for project skills.
Action *bool `json:"action,omitempty"`
ModelInvocable *bool `json:"model_invocable,omitempty"`
}
// OrganizationSkillRole is a role a user or group holds in an organization
// skill's access control list.
type OrganizationSkillRole string
const (
OrganizationSkillRoleRead OrganizationSkillRole = "read"
// OrganizationSkillRoleDeleted removes the principal's ACL entry when used
// in an update request.
OrganizationSkillRoleDeleted OrganizationSkillRole = ""
)
// OrganizationSkillACL is the resolved access control list of an
// organization skill.
type OrganizationSkillACL struct {
Users []OrganizationSkillUser `json:"users"`
Groups []OrganizationSkillGroup `json:"groups"`
}
// OrganizationSkillUser is a user entry in an organization skill ACL.
type OrganizationSkillUser struct {
MinimalUser
Role OrganizationSkillRole `json:"role" enums:"read"`
}
// OrganizationSkillGroup is a group entry in an organization skill ACL.
type OrganizationSkillGroup struct {
Group
Role OrganizationSkillRole `json:"role" enums:"read"`
}
// UpdateOrganizationSkillACLRequest is a sparse update of an organization
// skill ACL: only the listed principals change, and
// OrganizationSkillRoleDeleted removes an entry.
type UpdateOrganizationSkillACLRequest struct {
UserRoles map[string]OrganizationSkillRole `json:"user_roles,omitempty"`
GroupRoles map[string]OrganizationSkillRole `json:"group_roles,omitempty"`
}
// OrganizationSkillSettings is an organization's member opt-out setting for
// organization skills.
type OrganizationSkillSettings struct {
// MembersCanDisableSkills lets each organization member turn off
// organization skills for their own chats.
MembersCanDisableSkills bool `json:"members_can_disable_skills"`
}
// UpdateOrganizationSkillSettingsRequest is the payload for updating an
// organization's member opt-out setting for organization skills.
type UpdateOrganizationSkillSettingsRequest struct {
MembersCanDisableSkills bool `json:"members_can_disable_skills"`
}
// OrganizationSkillPreferences is an organization member's choices about the
// organization skills that load into their chats. The choices apply only
// while MembersCanDisableSkills is true.
type OrganizationSkillPreferences struct {
// MembersCanDisableSkills is the organization's setting.
MembersCanDisableSkills bool `json:"members_can_disable_skills"`
// AllSkillsDisabled turns off every organization skill, including
// skills added later. DisabledSkillIDs is kept and applies again when it
// is false.
AllSkillsDisabled bool `json:"all_skills_disabled"`
// DisabledSkillIDs lists the organization skills the member turned off.
DisabledSkillIDs []uuid.UUID `json:"disabled_skill_ids" format:"uuid"`
}
// UpdateOrganizationSkillPreferencesRequest is a sparse update of an
// organization member's organization skill choices. At least one field must
// be set, and only the listed skills change.
type UpdateOrganizationSkillPreferencesRequest struct {
AllSkillsDisabled *bool `json:"all_skills_disabled,omitempty"`
// Skills maps an organization skill ID to whether the skill is enabled
// for the member's chats.
Skills map[string]bool `json:"skills,omitempty"`
}
func userSkillsPath(user string) string {
return fmt.Sprintf("/api/experimental/users/%s/skills", url.PathEscape(user))
}
func userSkillPath(user string, name string) string {
return fmt.Sprintf("%s/%s", userSkillsPath(user), url.PathEscape(name))
}
// CreateUserSkill creates a user skill from raw Markdown content.
func (c *ExperimentalClient) CreateUserSkill(ctx context.Context, user string, req CreateSkillRequest) (Skill, error) {
res, err := c.Request(ctx, http.MethodPost, userSkillsPath(user), req)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// UserSkills lists user skill metadata for the specified user.
func (c *ExperimentalClient) UserSkills(ctx context.Context, user string) ([]SkillMetadata, error) {
res, err := c.Request(ctx, http.MethodGet, userSkillsPath(user), nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, ReadBodyAsError(res)
}
var skills []SkillMetadata
return skills, ReadBodyAsJSON(res, &skills)
}
// UserSkillByName returns a user skill by name.
func (c *ExperimentalClient) UserSkillByName(ctx context.Context, user string, name string) (Skill, error) {
res, err := c.Request(ctx, http.MethodGet, userSkillPath(user, name), nil)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// UpdateUserSkill updates a user skill's raw Markdown content or enabled state.
func (c *ExperimentalClient) UpdateUserSkill(ctx context.Context, user string, name string, req UpdateSkillRequest) (Skill, error) {
res, err := c.Request(ctx, http.MethodPatch, userSkillPath(user, name), req)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// DeleteUserSkill deletes a user skill by name.
func (c *ExperimentalClient) DeleteUserSkill(ctx context.Context, user string, name string) error {
res, err := c.Request(ctx, http.MethodDelete, userSkillPath(user, name), nil)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
}
func organizationSkillsPath(organizationID uuid.UUID) string {
return fmt.Sprintf("/api/experimental/organizations/%s/skills", organizationID)
}
func organizationSkillPath(organizationID uuid.UUID, name string) string {
return fmt.Sprintf("%s/%s", organizationSkillsPath(organizationID), url.PathEscape(name))
}
// CreateOrganizationSkill creates an organization skill from raw Markdown
// content.
func (c *ExperimentalClient) CreateOrganizationSkill(ctx context.Context, organizationID uuid.UUID, req CreateSkillRequest) (Skill, error) {
res, err := c.Request(ctx, http.MethodPost, organizationSkillsPath(organizationID), req)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// OrganizationSkills lists the organization skill metadata the caller can
// read.
func (c *ExperimentalClient) OrganizationSkills(ctx context.Context, organizationID uuid.UUID) ([]SkillMetadata, error) {
res, err := c.Request(ctx, http.MethodGet, organizationSkillsPath(organizationID), nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, ReadBodyAsError(res)
}
var skills []SkillMetadata
return skills, ReadBodyAsJSON(res, &skills)
}
// OrganizationSkillByName returns an organization skill by name.
func (c *ExperimentalClient) OrganizationSkillByName(ctx context.Context, organizationID uuid.UUID, name string) (Skill, error) {
res, err := c.Request(ctx, http.MethodGet, organizationSkillPath(organizationID, name), nil)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// UpdateOrganizationSkill updates an organization skill's raw Markdown content
// or enabled state.
func (c *ExperimentalClient) UpdateOrganizationSkill(ctx context.Context, organizationID uuid.UUID, name string, req UpdateSkillRequest) (Skill, error) {
res, err := c.Request(ctx, http.MethodPatch, organizationSkillPath(organizationID, name), req)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// DeleteOrganizationSkill deletes an organization skill by name.
func (c *ExperimentalClient) DeleteOrganizationSkill(ctx context.Context, organizationID uuid.UUID, name string) error {
res, err := c.Request(ctx, http.MethodDelete, organizationSkillPath(organizationID, name), nil)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
}
func organizationSkillSettingsPath(organizationID uuid.UUID) string {
return fmt.Sprintf("/api/experimental/organizations/%s/skill-settings", organizationID)
}
func organizationSkillPreferencesPath(organizationID uuid.UUID, user string) string {
return fmt.Sprintf("/api/experimental/organizations/%s/members/%s/skill-preferences", organizationID, url.PathEscape(user))
}
// OrganizationSkillSettings returns an organization's member opt-out setting
// for organization skills.
func (c *ExperimentalClient) OrganizationSkillSettings(ctx context.Context, organizationID uuid.UUID) (OrganizationSkillSettings, error) {
res, err := c.Request(ctx, http.MethodGet, organizationSkillSettingsPath(organizationID), nil)
if err != nil {
return OrganizationSkillSettings{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return OrganizationSkillSettings{}, ReadBodyAsError(res)
}
var settings OrganizationSkillSettings
return settings, ReadBodyAsJSON(res, &settings)
}
// UpdateOrganizationSkillSettings updates an organization's member opt-out
// setting for organization skills.
func (c *ExperimentalClient) UpdateOrganizationSkillSettings(ctx context.Context, organizationID uuid.UUID, req UpdateOrganizationSkillSettingsRequest) (OrganizationSkillSettings, error) {
res, err := c.Request(ctx, http.MethodPut, organizationSkillSettingsPath(organizationID), req)
if err != nil {
return OrganizationSkillSettings{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return OrganizationSkillSettings{}, ReadBodyAsError(res)
}
var settings OrganizationSkillSettings
return settings, ReadBodyAsJSON(res, &settings)
}
// OrganizationSkillPreferences returns an organization member's organization
// skill choices.
func (c *ExperimentalClient) OrganizationSkillPreferences(ctx context.Context, organizationID uuid.UUID, user string) (OrganizationSkillPreferences, error) {
res, err := c.Request(ctx, http.MethodGet, organizationSkillPreferencesPath(organizationID, user), nil)
if err != nil {
return OrganizationSkillPreferences{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return OrganizationSkillPreferences{}, ReadBodyAsError(res)
}
var prefs OrganizationSkillPreferences
return prefs, ReadBodyAsJSON(res, &prefs)
}
// UpdateOrganizationSkillPreferences applies a sparse update to an
// organization member's organization skill choices.
func (c *ExperimentalClient) UpdateOrganizationSkillPreferences(ctx context.Context, organizationID uuid.UUID, user string, req UpdateOrganizationSkillPreferencesRequest) (OrganizationSkillPreferences, error) {
res, err := c.Request(ctx, http.MethodPatch, organizationSkillPreferencesPath(organizationID, user), req)
if err != nil {
return OrganizationSkillPreferences{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return OrganizationSkillPreferences{}, ReadBodyAsError(res)
}
var prefs OrganizationSkillPreferences
return prefs, ReadBodyAsJSON(res, &prefs)
}
func chatProjectSkillPath(organizationID, projectID uuid.UUID, name string) string {
return chatProjectPath(organizationID, projectID) + "/skills/" + url.PathEscape(name)
}
// CreateChatProjectSkill creates a chat project skill from raw Markdown
// content.
func (c *ExperimentalClient) CreateChatProjectSkill(ctx context.Context, organizationID, projectID uuid.UUID, req CreateSkillRequest) (Skill, error) {
res, err := c.Request(ctx, http.MethodPost, chatProjectPath(organizationID, projectID)+"/skills", req)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// ChatProjectSkills lists a chat project's skill metadata.
func (c *ExperimentalClient) ChatProjectSkills(ctx context.Context, organizationID, projectID uuid.UUID) ([]SkillMetadata, error) {
res, err := c.Request(ctx, http.MethodGet, chatProjectPath(organizationID, projectID)+"/skills", nil)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, ReadBodyAsError(res)
}
var skills []SkillMetadata
return skills, ReadBodyAsJSON(res, &skills)
}
// ChatProjectSkill returns a chat project skill by name.
func (c *ExperimentalClient) ChatProjectSkill(ctx context.Context, organizationID, projectID uuid.UUID, name string) (Skill, error) {
res, err := c.Request(ctx, http.MethodGet, chatProjectSkillPath(organizationID, projectID, name), nil)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// UpdateChatProjectSkill updates a chat project skill's raw Markdown content
// or enabled state.
func (c *ExperimentalClient) UpdateChatProjectSkill(ctx context.Context, organizationID, projectID uuid.UUID, name string, req UpdateSkillRequest) (Skill, error) {
res, err := c.Request(ctx, http.MethodPatch, chatProjectSkillPath(organizationID, projectID, name), req)
if err != nil {
return Skill{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return Skill{}, ReadBodyAsError(res)
}
var skill Skill
return skill, ReadBodyAsJSON(res, &skill)
}
// DeleteChatProjectSkill deletes a chat project skill by name.
func (c *ExperimentalClient) DeleteChatProjectSkill(ctx context.Context, organizationID, projectID uuid.UUID, name string) error {
res, err := c.Request(ctx, http.MethodDelete, chatProjectSkillPath(organizationID, projectID, name), nil)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
}
// OrganizationSkillACL returns the resolved ACL of an organization skill.
func (c *ExperimentalClient) OrganizationSkillACL(ctx context.Context, organizationID uuid.UUID, name string) (OrganizationSkillACL, error) {
res, err := c.Request(ctx, http.MethodGet, organizationSkillPath(organizationID, name)+"/acl", nil)
if err != nil {
return OrganizationSkillACL{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return OrganizationSkillACL{}, ReadBodyAsError(res)
}
var acl OrganizationSkillACL
return acl, ReadBodyAsJSON(res, &acl)
}
// UpdateOrganizationSkillACL applies a sparse ACL update to an organization
// skill.
func (c *ExperimentalClient) UpdateOrganizationSkillACL(ctx context.Context, organizationID uuid.UUID, name string, req UpdateOrganizationSkillACLRequest) error {
res, err := c.Request(ctx, http.MethodPatch, organizationSkillPath(organizationID, name)+"/acl", req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
}
// OrganizationSkillACLAvailable returns the organization members and groups
// that can be added to an organization skill ACL.
func (c *ExperimentalClient) OrganizationSkillACLAvailable(ctx context.Context, organizationID uuid.UUID, name string, req UsersRequest) (ACLAvailable, error) {
res, err := c.Request(ctx, http.MethodGet,
organizationSkillPath(organizationID, name)+"/acl/available",
nil,
req.Pagination.asRequestOption(),
req.asRequestOption(),
)
if err != nil {
return ACLAvailable{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ACLAvailable{}, ReadBodyAsError(res)
}
var available ACLAvailable
return available, ReadBodyAsJSON(res, &available)
}