diff --git a/Taskfile.yml b/Taskfile.yml index fa9cb9cd..2988a63d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -85,6 +85,12 @@ tasks: vars: source: https://raw.githubusercontent.com/algolia/api-clients-automation/main/specs/bundled/search.yml destination: ./api/specs/search.yml + download-agent-studio-spec: + desc: Download the latest Agent Studio (RAG API) OpenAPI spec from a live endpoint + cmd: curl -fsSL -o {{ .destination }} {{ .source }} + vars: + source: https://agent-studio.eu.algolia.com/rag-openapi.json + destination: ./api/specs/agent-studio.json generate: desc: Generate command flags internal: true diff --git a/api/agentstudio/client.go b/api/agentstudio/client.go new file mode 100644 index 00000000..2278023a --- /dev/null +++ b/api/agentstudio/client.go @@ -0,0 +1,321 @@ +package agentstudio + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" +) + +// Client provides methods to interact with the Algolia Agent Studio (RAG) API. +type Client struct { + AppID string + APIKey string + BaseURL string + + client *http.Client +} + +// DefaultBaseURL builds the standard per-app server URL declared by the spec. +func DefaultBaseURL(appID string) string { + return fmt.Sprintf("https://%s.algolia.net/agent-studio/", appID) +} + +// NewClient returns a new Agent Studio API client using the default per-app +// server URL. +func NewClient(appID, apiKey string) *Client { + return &Client{ + AppID: appID, + APIKey: apiKey, + BaseURL: DefaultBaseURL(appID), + client: http.DefaultClient, + } +} + +// NewClientWithHTTPClient returns a new Agent Studio API client with a custom +// HTTP client. Tests use this to inject an httptest server. +func NewClientWithHTTPClient(appID, apiKey string, hc *http.Client) *Client { + return &Client{ + AppID: appID, + APIKey: apiKey, + BaseURL: DefaultBaseURL(appID), + client: hc, + } +} + +// request sends an HTTP request and unmarshals the response body to res when +// non-nil. Status >= 400 is converted to a formatted error. +func (c *Client) request( + res interface{}, + method, path string, + body interface{}, + urlParams map[string]string, +) error { + r, err := c.buildRequest(method, path, body, urlParams) + if err != nil { + return err + } + + resp, err := c.client.Do(r) + if err != nil { + return err + } + + if resp.StatusCode >= 400 { + var errResp ErrResponse + _ = unmarshalTo(resp, &errResp) + return fmt.Errorf("agentstudio: %s %s -> %d %s", method, path, resp.StatusCode, formatErr(errResp)) + } + + if res != nil { + if err := unmarshalTo(resp, res); err != nil { + return err + } + } else { + _ = resp.Body.Close() + } + + return nil +} + +func (c *Client) buildRequest( + method, path string, + body interface{}, + urlParams map[string]string, +) (*http.Request, error) { + url := strings.TrimRight(c.BaseURL, "/") + "/" + strings.TrimLeft(path, "/") + + var reader io.ReadCloser + if body != nil { + b, err := json.Marshal(body) + if err != nil { + return nil, err + } + reader = io.NopCloser(bytes.NewReader(b)) + } + + req, err := http.NewRequest(method, url, reader) + if err != nil { + return nil, err + } + + req.Header.Set("X-Algolia-Application-Id", c.AppID) + req.Header.Set("X-Algolia-API-Key", c.APIKey) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + + if len(urlParams) > 0 { + q := req.URL.Query() + for k, v := range urlParams { + q.Set(k, v) + } + req.URL.RawQuery = q.Encode() + } + + return req, nil +} + +func unmarshalTo(r *http.Response, v interface{}) error { + defer r.Body.Close() + return json.NewDecoder(r.Body).Decode(v) +} + +// formatErr renders an error response for human consumption. Prefers +// `message` (used by /completions), falls back to `detail` (used by CRUD +// endpoints), where detail may be a JSON string, an array of validation +// entries, or arbitrary JSON. +func formatErr(e ErrResponse) string { + if e.Message != "" { + return e.Message + } + if len(e.Detail) == 0 { + return "" + } + var s string + if err := json.Unmarshal(e.Detail, &s); err == nil { + return s + } + var entries []map[string]any + if err := json.Unmarshal(e.Detail, &entries); err == nil { + var parts []string + for _, en := range entries { + parts = append(parts, fmt.Sprintf("%v", en)) + } + return strings.Join(parts, "; ") + } + return string(e.Detail) +} + +func paginationParams(page, limit int) map[string]string { + params := map[string]string{} + if page > 0 { + params["page"] = strconv.Itoa(page) + } + if limit > 0 { + params["limit"] = strconv.Itoa(limit) + } + return params +} + +// Agents ----------------------------------------------------------------- + +func (c *Client) ListAgents(page, limit int) (*PaginatedAgentsResponse, error) { + var res PaginatedAgentsResponse + if err := c.request(&res, http.MethodGet, "1/agents", nil, paginationParams(page, limit)); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) GetAgent(id string) (*Agent, error) { + var res Agent + if err := c.request(&res, http.MethodGet, fmt.Sprintf("1/agents/%s", url.PathEscape(id)), nil, nil); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) CreateAgent(req AgentConfigCreate) (*Agent, error) { + var res Agent + if err := c.request(&res, http.MethodPost, "1/agents", req, nil); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) UpdateAgent(id string, req AgentConfigUpdate) (*Agent, error) { + var res Agent + if err := c.request(&res, http.MethodPatch, fmt.Sprintf("1/agents/%s", url.PathEscape(id)), req, nil); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) DeleteAgent(id string) error { + return c.request(nil, http.MethodDelete, fmt.Sprintf("1/agents/%s", url.PathEscape(id)), nil, nil) +} + +func (c *Client) DuplicateAgent(id string) (*Agent, error) { + var res Agent + if err := c.request(&res, http.MethodPost, fmt.Sprintf("1/agents/%s/duplicate", url.PathEscape(id)), nil, nil); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) PublishAgent(id string) (*Agent, error) { + var res Agent + if err := c.request(&res, http.MethodPost, fmt.Sprintf("1/agents/%s/publish", url.PathEscape(id)), nil, nil); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) UnpublishAgent(id string) (*Agent, error) { + var res Agent + if err := c.request(&res, http.MethodPost, fmt.Sprintf("1/agents/%s/unpublish", url.PathEscape(id)), nil, nil); err != nil { + return nil, err + } + return &res, nil +} + +// Completions ------------------------------------------------------------ + +// CompletionParams carries the query-string options for CreateCompletion. +// CompatibilityMode is required by the API; the rest are optional and default +// to the API's defaults when zero-valued. +type CompletionParams struct { + CompatibilityMode string // "ai-sdk-4" | "ai-sdk-5" (required) + Stream *bool // default true on the server; set false for non-streaming JSON + Cache *bool // default true on the server +} + +// CreateCompletion invokes an agent and returns the response body as an +// io.ReadCloser. The caller MUST close the returned reader. When stream=true +// (server default) the body is SSE bytes that arrive incrementally; when +// stream=false it is a single JSON document. Either way the body is streamed +// so we never buffer the full response in memory. +func (c *Client) CreateCompletion(agentID string, req AgentCompletionRequest, params CompletionParams) (io.ReadCloser, error) { + q := map[string]string{ + "compatibilityMode": params.CompatibilityMode, + } + if params.Stream != nil { + q["stream"] = strconv.FormatBool(*params.Stream) + } + if params.Cache != nil { + q["cache"] = strconv.FormatBool(*params.Cache) + } + + r, err := c.buildRequest(http.MethodPost, fmt.Sprintf("1/agents/%s/completions", url.PathEscape(agentID)), req, q) + if err != nil { + return nil, err + } + resp, err := c.client.Do(r) + if err != nil { + return nil, err + } + + if resp.StatusCode >= 400 { + body, _ := io.ReadAll(resp.Body) + _ = resp.Body.Close() + var errResp ErrResponse + _ = json.Unmarshal(body, &errResp) + return nil, fmt.Errorf("agentstudio: POST 1/agents/%s/completions -> %d %s", agentID, resp.StatusCode, formatErr(errResp)) + } + return resp.Body, nil +} + +// Conversations ---------------------------------------------------------- + +func (c *Client) ListConversations(agentID string, page, limit int) (*PaginatedConversationsResponse, error) { + var res PaginatedConversationsResponse + if err := c.request(&res, http.MethodGet, fmt.Sprintf("1/agents/%s/conversations", url.PathEscape(agentID)), nil, paginationParams(page, limit)); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) GetConversation(agentID, convID string) (*Conversation, error) { + var res Conversation + if err := c.request(&res, http.MethodGet, fmt.Sprintf("1/agents/%s/conversations/%s", url.PathEscape(agentID), url.PathEscape(convID)), nil, nil); err != nil { + return nil, err + } + return &res, nil +} + +func (c *Client) DeleteConversation(agentID, convID string) error { + return c.request(nil, http.MethodDelete, fmt.Sprintf("1/agents/%s/conversations/%s", url.PathEscape(agentID), url.PathEscape(convID)), nil, nil) +} + +func (c *Client) DeleteAllConversations(agentID string) error { + return c.request(nil, http.MethodDelete, fmt.Sprintf("1/agents/%s/conversations", url.PathEscape(agentID)), nil, nil) +} + +// ExportConversations returns the raw response body as a stream. The caller +// MUST close the returned reader. The spec does not constrain the response +// type (the dashboard call returns an attachment), so the body is streamed +// directly to avoid buffering large exports in memory. +func (c *Client) ExportConversations(agentID string) (io.ReadCloser, error) { + r, err := c.buildRequest(http.MethodGet, fmt.Sprintf("1/agents/%s/conversations/export", url.PathEscape(agentID)), nil, nil) + if err != nil { + return nil, err + } + resp, err := c.client.Do(r) + if err != nil { + return nil, err + } + + if resp.StatusCode >= 400 { + body, _ := io.ReadAll(resp.Body) + _ = resp.Body.Close() + var errResp ErrResponse + _ = json.Unmarshal(body, &errResp) + return nil, fmt.Errorf("agentstudio: GET conversations/export -> %d %s", resp.StatusCode, formatErr(errResp)) + } + + return resp.Body, nil +} diff --git a/api/agentstudio/client_test.go b/api/agentstudio/client_test.go new file mode 100644 index 00000000..c382a4cb --- /dev/null +++ b/api/agentstudio/client_test.go @@ -0,0 +1,140 @@ +package agentstudio + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestClient(handler http.Handler) (*httptest.Server, *Client) { + ts := httptest.NewServer(handler) + c := NewClientWithHTTPClient("APP", "KEY", ts.Client()) + c.BaseURL = ts.URL + "/" + return ts, c +} + +func TestRequest_SetsAuthHeaders(t *testing.T) { + ts, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "APP", r.Header.Get("X-Algolia-Application-Id")) + assert.Equal(t, "KEY", r.Header.Get("X-Algolia-API-Key")) + assert.Equal(t, "application/json", r.Header.Get("Content-Type")) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"data":[],"pagination":{"page":1,"limit":10,"totalCount":0,"totalPages":0}}`)) + })) + defer ts.Close() + + _, err := c.ListAgents(0, 0) + require.NoError(t, err) +} + +func TestListAgents_Success(t *testing.T) { + ts, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + assert.Equal(t, "/1/agents", r.URL.Path) + assert.Equal(t, "2", r.URL.Query().Get("page")) + assert.Equal(t, "5", r.URL.Query().Get("limit")) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{ + "data": [{"id":"a1","name":"Agent 1","status":"published","instructions":"hi","createdAt":"2026-01-01T00:00:00Z","updatedAt":"2026-01-01T00:00:00Z"}], + "pagination": {"page":2,"limit":5,"totalCount":1,"totalPages":1} + }`)) + })) + defer ts.Close() + + res, err := c.ListAgents(2, 5) + require.NoError(t, err) + assert.Len(t, res.Data, 1) + assert.Equal(t, "a1", res.Data[0].ID) + assert.Equal(t, 2, res.Pagination.Page) +} + +func TestGetAgent_NotFound(t *testing.T) { + ts, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNotFound) + _, _ = w.Write([]byte(`{"detail":"Agent not found"}`)) + })) + defer ts.Close() + + _, err := c.GetAgent("missing") + require.Error(t, err) + assert.Contains(t, err.Error(), "404") + assert.Contains(t, err.Error(), "Agent not found") +} + +func TestCreateAgent_Success(t *testing.T) { + ts, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method) + assert.Equal(t, "/1/agents", r.URL.Path) + var body AgentConfigCreate + require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.Equal(t, "Helper", body.Name) + assert.Equal(t, "be helpful", body.Instructions) + w.WriteHeader(http.StatusCreated) + _, _ = w.Write([]byte(`{"id":"a2","name":"Helper","status":"draft","instructions":"be helpful","createdAt":"2026-01-01T00:00:00Z","updatedAt":"2026-01-01T00:00:00Z"}`)) + })) + defer ts.Close() + + got, err := c.CreateAgent(AgentConfigCreate{Name: "Helper", Instructions: "be helpful"}) + require.NoError(t, err) + assert.Equal(t, "a2", got.ID) +} + +func TestDeleteAgent_NoContent(t *testing.T) { + ts, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodDelete, r.Method) + assert.Equal(t, "/1/agents/a3", r.URL.Path) + w.WriteHeader(http.StatusNoContent) + })) + defer ts.Close() + + require.NoError(t, c.DeleteAgent("a3")) +} + +func TestCreateCompletion_RawBody(t *testing.T) { + ts, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method) + assert.Equal(t, "/1/agents/a1/completions", r.URL.Path) + assert.Equal(t, "ai-sdk-5", r.URL.Query().Get("compatibilityMode")) + assert.Equal(t, "false", r.URL.Query().Get("stream")) + var req AgentCompletionRequest + require.NoError(t, json.NewDecoder(r.Body).Decode(&req)) + require.NotNil(t, req.ID) + assert.Equal(t, "conv-1", *req.ID) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"answer":"hello"}`)) + })) + defer ts.Close() + + convID := "conv-1" + stream := false + rc, err := c.CreateCompletion("a1", AgentCompletionRequest{ID: &convID}, CompletionParams{ + CompatibilityMode: "ai-sdk-5", + Stream: &stream, + }) + require.NoError(t, err) + defer rc.Close() + body, err := io.ReadAll(rc) + require.NoError(t, err) + assert.JSONEq(t, `{"answer":"hello"}`, string(body)) +} + +func TestFormatErr_DetailString(t *testing.T) { + out := formatErr(ErrResponse{Detail: json.RawMessage(`"plain message"`)}) + assert.Equal(t, "plain message", out) +} + +func TestFormatErr_DetailArray(t *testing.T) { + out := formatErr(ErrResponse{Detail: json.RawMessage(`[{"loc":["body","name"],"msg":"required","type":"missing"}]`)}) + assert.Contains(t, out, "required") +} + +func TestFormatErr_MessagePreferred(t *testing.T) { + // /completions returns {"message": "..."} instead of {"detail": ...} + out := formatErr(ErrResponse{Message: "Authentication failed for OpenAI"}) + assert.Equal(t, "Authentication failed for OpenAI", out) +} diff --git a/api/agentstudio/types.go b/api/agentstudio/types.go new file mode 100644 index 00000000..1da33068 --- /dev/null +++ b/api/agentstudio/types.go @@ -0,0 +1,109 @@ +package agentstudio + +import "encoding/json" + +// Agent is the full agent representation returned by the API. +// Complex nested fields (config, tools) are kept as raw JSON so the CLI does +// not have to model every provider/tool union variant. +type Agent struct { + ID string `json:"id"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + Status string `json:"status"` + ProviderID *string `json:"providerId,omitempty"` + Model *string `json:"model,omitempty"` + Instructions string `json:"instructions"` + SystemPrompt *string `json:"systemPrompt,omitempty"` + Config json.RawMessage `json:"config,omitempty"` + Tools json.RawMessage `json:"tools,omitempty"` + TemplateType *string `json:"templateType,omitempty"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + LastUsedAt *string `json:"lastUsedAt,omitempty"` +} + +// PaginationMetadata mirrors the spec. +type PaginationMetadata struct { + Page int `json:"page"` + Limit int `json:"limit"` + TotalCount int `json:"totalCount"` + TotalPages int `json:"totalPages"` +} + +// PaginatedAgentsResponse is the response of GET /1/agents. +type PaginatedAgentsResponse struct { + Data []Agent `json:"data"` + Pagination PaginationMetadata `json:"pagination"` +} + +// AgentConfigCreate is the request body for POST /1/agents. +// Free-form / union-typed fields are passed as raw JSON. +type AgentConfigCreate struct { + Name string `json:"name"` + Description *string `json:"description,omitempty"` + ProviderID *string `json:"providerId,omitempty"` + Model *string `json:"model,omitempty"` + Instructions string `json:"instructions"` + SystemPrompt *string `json:"systemPrompt,omitempty"` + TemplateType *string `json:"templateType,omitempty"` + Config json.RawMessage `json:"config,omitempty"` + Tools json.RawMessage `json:"tools,omitempty"` +} + +// AgentConfigUpdate is the request body for PATCH /1/agents/{id}. All fields +// are optional. +type AgentConfigUpdate struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + ProviderID *string `json:"providerId,omitempty"` + Model *string `json:"model,omitempty"` + Instructions *string `json:"instructions,omitempty"` + SystemPrompt *string `json:"systemPrompt,omitempty"` + TemplateType *string `json:"templateType,omitempty"` + Config json.RawMessage `json:"config,omitempty"` + Tools json.RawMessage `json:"tools,omitempty"` +} + +// AgentCompletionRequest is the body of POST /1/agents/{id}/completions. +type AgentCompletionRequest struct { + ID *string `json:"id,omitempty"` + Configuration json.RawMessage `json:"configuration,omitempty"` + Messages json.RawMessage `json:"messages,omitempty"` + Algolia json.RawMessage `json:"algolia,omitempty"` + ToolApprovals json.RawMessage `json:"toolApprovals,omitempty"` +} + +// Conversation is a flattened view used by both list and get endpoints. +// Messages is only populated by ConversationFullResponse-shaped payloads. +type Conversation struct { + ID string `json:"id"` + AgentID string `json:"agentId"` + Title *string `json:"title,omitempty"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + LastActivityAt *string `json:"lastActivityAt,omitempty"` + UserToken *string `json:"userToken,omitempty"` + IsFromDashboard *bool `json:"isFromDashboard,omitempty"` + MessageCount *int `json:"messageCount,omitempty"` + TotalInputTokens *int `json:"totalInputTokens,omitempty"` + TotalOutputTokens *int `json:"totalOutputTokens,omitempty"` + TotalTokens *int `json:"totalTokens,omitempty"` + ConversationMetadata json.RawMessage `json:"conversationMetadata,omitempty"` + Feedback json.RawMessage `json:"feedback,omitempty"` + Messages json.RawMessage `json:"messages,omitempty"` +} + +// PaginatedConversationsResponse is the response of GET /1/agents/{id}/conversations. +type PaginatedConversationsResponse struct { + Data []Conversation `json:"data"` + Pagination PaginationMetadata `json:"pagination"` +} + +// ErrResponse models the error envelope. The completions endpoint returns +// `{message}` while CRUD endpoints return the FastAPI-style `{detail}` +// (string for auth errors, array for validation errors). We surface +// whichever is set. +type ErrResponse struct { + Detail json.RawMessage `json:"detail,omitempty"` + Message string `json:"message,omitempty"` +} diff --git a/api/specs/agent-studio.json b/api/specs/agent-studio.json new file mode 100644 index 00000000..1e815fc8 --- /dev/null +++ b/api/specs/agent-studio.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","info":{"title":"RAG API","summary":"Algolia's GenAI Toolkit API, building blocks for your dynamic LLM-enabled experiences.","description":"Build generative AI experiences with your Algolia data. Combine large language models with Algolia’s Search API using Retrieval Augmented Generation (RAG) techniques.","version":"0.1.0"},"servers":[{"url":"https://{APPLICATION_ID}.algolia.net/agent-studio","description":"Agent Studio API","variables":{"APPLICATION_ID":{"default":"EXAMPLE","description":"Your Algolia application ID"}}}],"paths":{"/1/agents":{"get":{"tags":["Agents"],"summary":"List Agents","description":"List all agents with pagination and filtering.","operationId":"list_agents_1_agents_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Items per page","default":10,"title":"Limit"},"description":"Items per page"},{"name":"providerId","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filter by provider id","title":"Providerid"},"description":"Filter by provider id"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedAgentsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}},"post":{"tags":["Agents"],"summary":"Create Agent","description":"Create a new agent.","operationId":"create_agent_1_agents_post","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentConfigCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/{agent_id}/duplicate":{"post":{"tags":["Agents"],"summary":"Duplicate Agent","description":"Duplicate an existing agent.","operationId":"duplicate_agent_1_agents__agent_id__duplicate_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`","x-hidden":true}}},"/1/agents/{agent_id}":{"get":{"tags":["Agents"],"summary":"Get Agent","description":"Retrieve details of the specified agent.","operationId":"get_agent_1_agents__agent_id__get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}},"patch":{"tags":["Agents"],"summary":"Update Agent","description":"Update the specified agent.","operationId":"update_agent_1_agents__agent_id__patch","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentConfigUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}},"delete":{"tags":["Agents"],"summary":"Delete Agent","description":"Delete the specified agent.","operationId":"delete_agent_1_agents__agent_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/{agent_id}/cache":{"delete":{"tags":["Agents"],"summary":"Invalidate Agent Cache","description":"Invalidate cached completions for this agent. Filter with `before` (exclusive).","operationId":"invalidate_agent_cache_1_agents__agent_id__cache_delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Delete entries strictly before this date (exclusive, YYYY-MM-DD)","title":"Before"},"description":"Delete entries strictly before this date (exclusive, YYYY-MM-DD)"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/{agent_id}/publish":{"post":{"tags":["Agents"],"summary":"Publish Agent","description":"Publish the specified agent.","operationId":"publish_agent_1_agents__agent_id__publish_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/{agent_id}/unpublish":{"post":{"tags":["Agents"],"summary":"Unpublish Agent","description":"Unpublish the specified agent.","operationId":"unpublish_agent_1_agents__agent_id__unpublish_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/agents/{agent_id}/memorize":{"post":{"tags":["Agents","Agents"],"summary":"Agent Memorize","description":"Extract semantic memories from conversation and save them automatically.","operationId":"agent_memorize_1_agents_agents__agent_id__memorize_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}},{"name":"X-Algolia-User-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Algolia-User-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentMemorizeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentMemorizeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-hidden":true}},"/1/agents/agents/{agent_id}/ponder":{"post":{"tags":["Agents","Agents"],"summary":"Agent Ponder","description":"Extract episodic memories (OTAR episodes) and save them automatically.","operationId":"agent_ponder_1_agents_agents__agent_id__ponder_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}},{"name":"X-Algolia-User-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Algolia-User-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentPonderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentPonderResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-hidden":true}},"/1/agents/agents/{agent_id}/consolidate":{"post":{"tags":["Agents","Agents"],"summary":"Agent Consolidate","description":"Consolidate an agent's memories by loading existing ones automatically.","operationId":"agent_consolidate_1_agents_agents__agent_id__consolidate_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}},{"name":"X-Algolia-User-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Algolia-User-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentConsolidateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentConsolidateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-hidden":true}},"/1/agents/{agent_id}/completions":{"post":{"tags":["Completions","Completions"],"summary":"Create Completion","description":"Create a completion for the specified agent.\n\nThis endpoint handles two types of requests:\n1. Normal completion request: User message -> Agent response\n2. Tool approval response: User approval -> Execute tool -> Agent response\n\nTool Approval Flow (for MCP tools with requiresApproval: true):\n- Request 1: User sends message -> Agent requests tool call -> Return approval request\n- Request 2: User approves -> Execute tool -> Agent continues with result","operationId":"create_completion_1_agents__agent_id__completions_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"const":"test","type":"string"}],"title":"Agent Id"}},{"name":"compatibilityMode","in":"query","required":true,"schema":{"$ref":"#/components/schemas/CompatibilityMode","description":"Compatibility mode for the completion API"},"description":"Compatibility mode for the completion API"},{"name":"stream","in":"query","required":false,"schema":{"type":"boolean","description":"Whether to stream the response or not","default":true,"title":"Stream"},"description":"Whether to stream the response or not"},{"name":"cache","in":"query","required":false,"schema":{"type":"boolean","description":"Use cached responses if available","default":true,"title":"Cache"},"description":"Use cached responses if available"},{"name":"memory","in":"query","required":false,"schema":{"anyOf":[{"const":false,"type":"boolean"},{"type":"null"}],"description":"Set to false to disable memory (enabled by default)","title":"Memory"},"description":"Set to false to disable memory (enabled by default)"},{"name":"analytics","in":"query","required":false,"schema":{"type":"boolean","description":"Set to false to skip Agent Studio analytics for this completion (default: true).","default":true,"title":"Analytics"},"description":"Set to false to skip Agent Studio analytics for this completion (default: true)."},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}},{"name":"X-Algolia-Secure-User-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Algolia-Secure-User-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCompletionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `search`"}}},"/1/agents/{agent_id}/conversations/{conversation_id}":{"delete":{"tags":["Conversations"],"summary":"Delete Conversation","description":"Deletes the conversation with the given ID.","operationId":"delete_conversation_1_agents__agent_id__conversations__conversation_id__delete","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}},"get":{"tags":["Conversations"],"summary":"Get Conversation","description":"Retrieves the conversation and its messages for the given ID.","operationId":"get_conversation_1_agents__agent_id__conversations__conversation_id__get","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"agent_id","in":"path","required":true,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"const":"test","type":"string"}],"title":"Agent Id"}},{"name":"includeFeedback","in":"query","required":false,"schema":{"type":"boolean","description":"Include feedback for the conversation","default":false,"title":"Includefeedback"},"description":"Include feedback for the conversation"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}},{"name":"X-Algolia-Secure-User-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Algolia-Secure-User-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationFullResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}}},"/1/agents/{agent_id}/conversations":{"delete":{"tags":["Conversations"],"summary":"Delete Conversations","description":"Deletes the conversations matching the given filers.","operationId":"delete_conversations_1_agents__agent_id__conversations_delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"startDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter conversations created after this date (format: YYYY-MM-DD)","title":"Startdate"},"description":"Filter conversations created after this date (format: YYYY-MM-DD)"},{"name":"endDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter conversations created before this date (format: YYYY-MM-DD)","title":"Enddate"},"description":"Filter conversations created before this date (format: YYYY-MM-DD)"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}},"get":{"tags":["Conversations"],"summary":"List Conversations","description":"Retrieves the conversations for the given agent ID.","operationId":"list_conversations_1_agents__agent_id__conversations_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"const":"test","type":"string"}],"title":"Agent Id"}},{"name":"startDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter conversations created after this date (format: YYYY-MM-DD)","title":"Startdate"},"description":"Filter conversations created after this date (format: YYYY-MM-DD)"},{"name":"endDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter conversations created before this date (format: YYYY-MM-DD)","title":"Enddate"},"description":"Filter conversations created before this date (format: YYYY-MM-DD)"},{"name":"includeFeedback","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Include feedback per conversation","default":false,"title":"Includefeedback"},"description":"Include feedback per conversation"},{"name":"feedbackVote","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":1,"minimum":0},{"type":"null"}],"description":"Filter by feedback value (requires includeFeedback=true)","title":"Feedbackvote"},"description":"Filter by feedback value (requires includeFeedback=true)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page","default":20,"title":"Limit"},"description":"Items per page"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}},{"name":"X-Algolia-Secure-User-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Algolia-Secure-User-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedConversationsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}}},"/1/agents/{agent_id}/conversations/export":{"get":{"tags":["Conversations"],"summary":"Export Conversations","description":"Exports all conversations based on the passed filters","operationId":"export_conversations_1_agents__agent_id__conversations_export_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"startDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter conversations created after this date (format: YYYY-MM-DD)","title":"Startdate"},"description":"Filter conversations created after this date (format: YYYY-MM-DD)"},{"name":"endDate","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Filter conversations created before this date (format: YYYY-MM-DD)","title":"Enddate"},"description":"Filter conversations created before this date (format: YYYY-MM-DD)"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}}},"/1/providers/models":{"get":{"tags":["Providers"],"summary":"Get Provider Models","operationId":"get_provider_models_1_providers_models_get","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"title":"Response Get Provider Models 1 Providers Models Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}}},"/1/providers/models/defaults":{"get":{"tags":["Providers"],"summary":"Get Provider Fast Model Defaults","description":"Get the default fast model for each provider, used for suggestions.","operationId":"get_provider_fast_model_defaults_1_providers_models_defaults_get","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Get Provider Fast Model Defaults 1 Providers Models Defaults Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"},"x-hidden":true}},"/1/providers":{"post":{"tags":["Providers"],"summary":"Create Provider","operationId":"create_provider_1_providers_post","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderAuthenticationCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderAuthenticationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}},"get":{"tags":["Providers"],"summary":"List Providers","operationId":"list_providers_1_providers_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Items per page","default":10,"title":"Limit"},"description":"Items per page"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedProviderAuthenticationsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}}},"/1/providers/{provider_id}":{"get":{"tags":["Providers"],"summary":"Get Provider","operationId":"get_provider_1_providers__provider_id__get","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Provider Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderAuthenticationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}},"patch":{"tags":["Providers"],"summary":"Update Provider","operationId":"update_provider_1_providers__provider_id__patch","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Provider Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderAuthenticationPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderAuthenticationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}},"delete":{"tags":["Providers"],"summary":"Delete Provider","operationId":"delete_provider_1_providers__provider_id__delete","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Provider Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/providers/{provider_id}/models":{"get":{"tags":["Providers"],"summary":"Get Provider Models By Id","description":"Get available models for a specific provider.","operationId":"get_provider_models_by_id_1_providers__provider_id__models_get","parameters":[{"name":"provider_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Provider Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"},"title":"Response Get Provider Models By Id 1 Providers Provider Id Models Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}}},"/1/configuration":{"get":{"tags":["Configurations"],"summary":"Get Configuration","operationId":"Get_Configuration_1_configuration_get","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}},"patch":{"tags":["Configurations"],"summary":"Patch Configuration","operationId":"Patch_Configuration_1_configuration_patch","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationConfigPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}}},"/1/agents/{agent_id}/allowed-domains":{"get":{"tags":["Allowed domains"],"summary":"List Allowed Domains","description":"List all allowed domain patterns for this agent.","operationId":"list_allowed_domains_1_agents__agent_id__allowed_domains_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}},"post":{"tags":["Allowed domains"],"summary":"Create Allowed Domain","description":"Add a single allowed domain pattern (e.g. https://app.example.com or *.example.com).","operationId":"create_allowed_domain_1_agents__agent_id__allowed_domains_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/{agent_id}/allowed-domains/bulk":{"post":{"tags":["Allowed domains"],"summary":"Bulk Insert Allowed Domains","description":"Add multiple allowed domain patterns. Duplicates are skipped.","operationId":"bulk_insert_allowed_domains_1_agents__agent_id__allowed_domains_bulk_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainBulkInsert"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}},"delete":{"tags":["Allowed domains"],"summary":"Bulk Delete Allowed Domains","description":"Delete allowed domains by id list.","operationId":"bulk_delete_allowed_domains_1_agents__agent_id__allowed_domains_bulk_delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainBulkDelete"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/agents/{agent_id}/allowed-domains/{domain_id}":{"get":{"tags":["Allowed domains"],"summary":"Get Allowed Domain","description":"Get a single allowed domain by id.","operationId":"get_allowed_domain_1_agents__agent_id__allowed_domains__domain_id__get","parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Domain Id"}},{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowedDomainResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings`"}},"delete":{"tags":["Allowed domains"],"summary":"Delete Allowed Domain","description":"Remove an allowed domain by id.","operationId":"delete_allowed_domain_1_agents__agent_id__allowed_domains__domain_id__delete","parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Domain Id"}},{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `editSettings`"}}},"/1/user-data/{user_token}":{"get":{"tags":["User Data"],"summary":"Get Data By User Token","description":"Retrieves all memories, conversations and their messages for the given user token.","operationId":"get_data_by_user_token_1_user_data__user_token__get","parameters":[{"name":"user_token","in":"path","required":true,"schema":{"type":"string","title":"User Token"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDataResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}},"delete":{"tags":["User Data"],"summary":"Delete Data By User Token","description":"Permanently deletes all messages for the given user token. Does not delete conversations.","operationId":"delete_data_by_user_token_1_user_data__user_token__delete","parameters":[{"name":"user_token","in":"path","required":true,"schema":{"type":"string","title":"User Token"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `logs`"}}},"/1/secret-keys":{"get":{"tags":["Secret Keys"],"summary":"List Secret Keys","operationId":"list_secret_keys_1_secret_keys_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Items per page","default":10,"title":"Limit"},"description":"Items per page"},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedSecretKeysResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings` or Admin API key to see unredacted values"}},"post":{"tags":["Secret Keys"],"summary":"Create Secret Key","operationId":"create_secret_key_1_secret_keys_post","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretKeyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Requires Admin API key**"}}},"/1/secret-keys/{secret_key_id}":{"get":{"tags":["Secret Keys"],"summary":"Get Secret Key","operationId":"get_secret_key_1_secret_keys__secret_key_id__get","parameters":[{"name":"secret_key_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Secret Key Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `settings` or Admin API key to see unredacted values"}},"patch":{"tags":["Secret Keys"],"summary":"Patch Secret Key","operationId":"patch_secret_key_1_secret_keys__secret_key_id__patch","parameters":[{"name":"secret_key_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Secret Key Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretKeyPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Requires Admin API key**"}},"delete":{"tags":["Secret Keys"],"summary":"Delete Secret Key","operationId":"delete_secret_key_1_secret_keys__secret_key_id__delete","parameters":[{"name":"secret_key_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Secret Key Id"}},{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Requires Admin API key**"}}},"/1/feedback":{"post":{"tags":["Feedback"],"summary":"Create Feedback","description":"Create new feedback entry.","operationId":"create_feedback_1_feedback_post","parameters":[{"name":"X-Algolia-API-Key","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Api-Key"}},{"name":"X-Algolia-Application-Id","in":"header","required":true,"schema":{"type":"string","title":"X-Algolia-Application-Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackCreationRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-mint":{"content":"**Required ACL**: `search`"}}},"/status":{"get":{"tags":["Internal"],"summary":"Read Root","description":"Path to check status of the API.","operationId":"read_root_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Read Root Status Get"}}}}},"x-hidden":true}}},"components":{"schemas":{"AdvancedSyntaxFeatures":{"type":"string","enum":["exactPhrase","excludeWords"],"title":"AdvancedSyntaxFeatures","description":"AdvancedSyntaxFeatures"},"AgentCompletionAlgoliaParams":{"properties":{"mcpServers":{"anyOf":[{"additionalProperties":{"additionalProperties":{"additionalProperties":{"type":"string"},"type":"object"},"type":"object"},"type":"object"},{"type":"null"}],"title":"Mcpservers"},"searchParameters":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/SearchParametersOverrides"},"type":"object"},{"type":"null"}],"title":"Searchparameters","description":"Per-index runtime Algolia searchParameters keyed by index name. Allowed keys per index: ['attributes_to_retrieve', 'distinct', 'enable_personalization', 'filters', 'personalization_impact', 'restrict_searchable_attributes', 'user_token'].","examples":[{"products":{"attributesToRetrieve":["title","price"],"distinct":false,"enablePersonalization":true,"filters":"category:electronics","personalizationImpact":100,"userToken":"user-123"}}]}},"type":"object","title":"AgentCompletionAlgoliaParams"},"AgentCompletionRequest":{"properties":{"configuration":{"anyOf":[{"$ref":"#/components/schemas/AgentTestConfiguration"},{"type":"null"}]},"messages":{"anyOf":[{"oneOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV4"},{"$ref":"#/components/schemas/AssistantMessageV4"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV4","user":"#/components/schemas/UserMessageV4"}}},"type":"array"},{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV5"},{"$ref":"#/components/schemas/AssistantMessageV5"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV5","user":"#/components/schemas/UserMessageV5"}}},"type":"array"}]},{"type":"null"}],"title":"Messages"},"id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Id","description":"Optional conversation id."},"algolia":{"anyOf":[{"$ref":"#/components/schemas/AgentCompletionAlgoliaParams"},{"type":"null"}]},"toolApprovals":{"anyOf":[{"additionalProperties":{"additionalProperties":true,"type":"object"},"type":"object"},{"type":"null"}],"title":"Toolapprovals"}},"type":"object","title":"AgentCompletionRequest","description":"Request model for creating a completion for an assistant."},"AgentConfigCreate":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"providerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Providerid"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"instructions":{"type":"string","title":"Instructions","description":"The agent prompt: defines the agent's role, tone, and goals. Guides how it answers using the provided context. Corresponds to the 'Agent prompt' field in the dashboard."},"systemPrompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Systemprompt","description":"The system prompt: defines system-level rules and constraints. Guides how the agent uses tools, features, and generates context. Prepended before `instructions` in the final prompt sent to the LLM. Typically injected by an agent template — modify with caution, as changes may affect behavior, tool usage, or response accuracy. Corresponds to the 'System prompt' field in the dashboard."},"templateType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Templatetype"},"config":{"additionalProperties":true,"type":"object","title":"Config","default":{},"examples":["sendUsage: true","sendReasoning: true","temperature: 0.7","max_tokens: 1500","reasoning: {summary: auto}","features: [memory]"]},"tools":{"items":{"oneOf":[{"$ref":"#/components/schemas/ClientSideToolConfig"},{"$ref":"#/components/schemas/AlgoliaSearchToolConfig-Input"},{"$ref":"#/components/schemas/AlgoliaRecommendToolConfig-Input"},{"$ref":"#/components/schemas/AlgoliaDisplayResultsToolConfig"},{"$ref":"#/components/schemas/McpServerToolConfig-Input"},{"$ref":"#/components/schemas/UnknownToolConfig"}],"discriminator":{"propertyName":"type","mapping":{"algolia_display_results":"#/components/schemas/AlgoliaDisplayResultsToolConfig","algolia_recommend":"#/components/schemas/AlgoliaRecommendToolConfig-Input","algolia_search_index":"#/components/schemas/AlgoliaSearchToolConfig-Input","client_side":"#/components/schemas/ClientSideToolConfig","mcp_tools":"#/components/schemas/McpServerToolConfig-Input","unknown":"#/components/schemas/UnknownToolConfig"}}},"type":"array","title":"Tools"}},"type":"object","required":["name","instructions"],"title":"AgentConfigCreate"},"AgentConfigUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"providerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Providerid"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instructions","description":"The agent prompt: defines the agent's role, tone, and goals. Guides how it answers using the provided context. Corresponds to the 'Agent prompt' field in the dashboard."},"systemPrompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Systemprompt","description":"The system prompt: defines system-level rules and constraints. Guides how the agent uses tools, features, and generates context. Prepended before `instructions` in the final prompt sent to the LLM. Typically injected by an agent template — modify with caution, as changes may affect behavior, tool usage, or response accuracy. Corresponds to the 'System prompt' field in the dashboard."},"config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Config"},"tools":{"anyOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/ClientSideToolConfig"},{"$ref":"#/components/schemas/AlgoliaSearchToolConfig-Input"},{"$ref":"#/components/schemas/AlgoliaRecommendToolConfig-Input"},{"$ref":"#/components/schemas/AlgoliaDisplayResultsToolConfig"},{"$ref":"#/components/schemas/McpServerToolConfig-Input"},{"$ref":"#/components/schemas/UnknownToolConfig"}],"discriminator":{"propertyName":"type","mapping":{"algolia_display_results":"#/components/schemas/AlgoliaDisplayResultsToolConfig","algolia_recommend":"#/components/schemas/AlgoliaRecommendToolConfig-Input","algolia_search_index":"#/components/schemas/AlgoliaSearchToolConfig-Input","client_side":"#/components/schemas/ClientSideToolConfig","mcp_tools":"#/components/schemas/McpServerToolConfig-Input","unknown":"#/components/schemas/UnknownToolConfig"}}},"type":"array"},{"type":"null"}],"title":"Tools"},"templateType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Templatetype"}},"type":"object","title":"AgentConfigUpdate"},"AgentConsolidateRequest":{"properties":{"providerID":{"type":"string","title":"Providerid","description":"Provider UUID for LLM credentials"},"model":{"type":"string","title":"Model","description":"Model name (e.g., gpt-4o-mini)"},"messages":{"oneOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV4"},{"$ref":"#/components/schemas/AssistantMessageV4"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV4","user":"#/components/schemas/UserMessageV4"}}},"type":"array"},{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV5"},{"$ref":"#/components/schemas/AssistantMessageV5"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV5","user":"#/components/schemas/UserMessageV5"}}},"type":"array"}],"title":"Messages","description":"Conversation in AI SDK v4/v5 format"},"targetMemories":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Targetmemories","description":"Maximum memories to extract/consolidate in this operation","default":5},"instructions":{"anyOf":[{"type":"string","maxLength":50000},{"type":"null"}],"title":"Instructions","description":"Optional domain-specific instructions to guide extraction or consolidation"},"wait":{"type":"boolean","title":"Wait","description":"Wait for Algolia indexing to complete before returning","default":false},"memoryType":{"$ref":"#/components/schemas/MemoryType","description":"Which memory type to consolidate (semantic or episodic)","default":"semantic"},"maxExisting":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Maxexisting","description":"Maximum number of existing memories to load for consolidation","default":50}},"type":"object","required":["providerID","model","messages"],"title":"AgentConsolidateRequest","description":"Agent endpoint request for automatic memory consolidation."},"AgentConsolidateResponse":{"properties":{"savedMemories":{"items":{"$ref":"#/components/schemas/MemoryRecord"},"type":"array","title":"Savedmemories","description":"Memories saved/updated"},"deletedIds":{"items":{"type":"string"},"type":"array","title":"Deletedids","description":"Memory object IDs deleted during consolidation"},"saveTaskId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Savetaskid","description":"Algolia task ID for save batch"},"deleteTaskIds":{"items":{"type":"string"},"type":"array","title":"Deletetaskids","description":"Algolia task IDs for deletes"},"message":{"type":"string","title":"Message","description":"Summary message of consolidation results"}},"type":"object","required":["message"],"title":"AgentConsolidateResponse","description":"Response model for agent consolidate endpoint."},"AgentMemorizeRequest":{"properties":{"providerID":{"type":"string","title":"Providerid","description":"Provider UUID for LLM credentials"},"model":{"type":"string","title":"Model","description":"Model name (e.g., gpt-4o-mini)"},"messages":{"oneOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV4"},{"$ref":"#/components/schemas/AssistantMessageV4"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV4","user":"#/components/schemas/UserMessageV4"}}},"type":"array"},{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV5"},{"$ref":"#/components/schemas/AssistantMessageV5"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV5","user":"#/components/schemas/UserMessageV5"}}},"type":"array"}],"title":"Messages","description":"Conversation in AI SDK v4/v5 format"},"targetMemories":{"type":"integer","maximum":50.0,"minimum":1.0,"title":"Targetmemories","description":"Maximum memories to extract/consolidate in this operation","default":5},"instructions":{"anyOf":[{"type":"string","maxLength":50000},{"type":"null"}],"title":"Instructions","description":"Optional domain-specific instructions to guide extraction or consolidation"},"wait":{"type":"boolean","title":"Wait","description":"Wait for Algolia indexing to complete before returning","default":false}},"type":"object","required":["providerID","model","messages"],"title":"AgentMemorizeRequest","description":"Agent endpoint request that extracts and saves semantic memories."},"AgentMemorizeResponse":{"properties":{"savedMemories":{"items":{"$ref":"#/components/schemas/MemoryRecord"},"type":"array","title":"Savedmemories","description":"Memories saved to Algolia"},"taskId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taskid","description":"Algolia task ID for the save operation"},"message":{"type":"string","title":"Message","description":"Summary of the operation outcome"}},"type":"object","required":["message"],"title":"AgentMemorizeResponse","description":"Response model for agent memorize endpoint."},"AgentPonderRequest":{"properties":{"providerID":{"type":"string","title":"Providerid","description":"Provider UUID for LLM credentials"},"model":{"type":"string","title":"Model","description":"Model name (e.g., gpt-4o-mini)"},"messages":{"oneOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV4"},{"$ref":"#/components/schemas/AssistantMessageV4"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV4","user":"#/components/schemas/UserMessageV4"}}},"type":"array"},{"items":{"oneOf":[{"$ref":"#/components/schemas/UserMessageV5"},{"$ref":"#/components/schemas/AssistantMessageV5"}],"discriminator":{"propertyName":"role","mapping":{"assistant":"#/components/schemas/AssistantMessageV5","user":"#/components/schemas/UserMessageV5"}}},"type":"array"}],"title":"Messages","description":"Conversation in AI SDK v4/v5 format"},"targetMemories":{"type":"integer","maximum":5.0,"minimum":1.0,"title":"Targetmemories","description":"Maximum episodic memories to extract","default":1},"instructions":{"anyOf":[{"type":"string","maxLength":50000},{"type":"null"}],"title":"Instructions","description":"Optional domain-specific instructions to guide extraction or consolidation"},"wait":{"type":"boolean","title":"Wait","description":"Wait for Algolia indexing to complete before returning","default":false}},"type":"object","required":["providerID","model","messages"],"title":"AgentPonderRequest","description":"Agent endpoint request that extracts and saves episodic memories."},"AgentPonderResponse":{"properties":{"savedMemories":{"items":{"$ref":"#/components/schemas/MemoryRecord"},"type":"array","title":"Savedmemories","description":"Memories saved to Algolia"},"taskId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taskid","description":"Algolia task ID for the save operation"},"message":{"type":"string","title":"Message","description":"Summary of the operation outcome"}},"type":"object","required":["message"],"title":"AgentPonderResponse","description":"Response model for agent ponder endpoint (episodic save)."},"AgentTestConfiguration":{"properties":{"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"providerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Providerid"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"instructions":{"type":"string","title":"Instructions"},"systemPrompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Systemprompt"},"config":{"additionalProperties":true,"type":"object","title":"Config"},"tools":{"items":{"oneOf":[{"$ref":"#/components/schemas/ClientSideToolConfig"},{"$ref":"#/components/schemas/AlgoliaSearchToolConfig-Input"},{"$ref":"#/components/schemas/AlgoliaRecommendToolConfig-Input"},{"$ref":"#/components/schemas/AlgoliaDisplayResultsToolConfig"},{"$ref":"#/components/schemas/McpServerToolConfig-Input"},{"$ref":"#/components/schemas/UnknownToolConfig"}],"discriminator":{"propertyName":"type","mapping":{"algolia_display_results":"#/components/schemas/AlgoliaDisplayResultsToolConfig","algolia_recommend":"#/components/schemas/AlgoliaRecommendToolConfig-Input","algolia_search_index":"#/components/schemas/AlgoliaSearchToolConfig-Input","client_side":"#/components/schemas/ClientSideToolConfig","mcp_tools":"#/components/schemas/McpServerToolConfig-Input","unknown":"#/components/schemas/UnknownToolConfig"}}},"type":"array","title":"Tools"}},"type":"object","required":["instructions","config","tools"],"title":"AgentTestConfiguration","description":"Dynamic configuration for testing agents."},"AgentWithVersionResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","enum":["draft","published"],"title":"Status"},"providerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Providerid"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"instructions":{"type":"string","title":"Instructions"},"systemPrompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Systemprompt"},"config":{"additionalProperties":true,"type":"object","title":"Config"},"tools":{"items":{"oneOf":[{"$ref":"#/components/schemas/ClientSideToolConfig"},{"$ref":"#/components/schemas/AlgoliaSearchToolConfig-Output"},{"$ref":"#/components/schemas/AlgoliaRecommendToolConfig-Output"},{"$ref":"#/components/schemas/AlgoliaDisplayResultsToolConfig"},{"$ref":"#/components/schemas/McpServerToolConfig-Output"},{"$ref":"#/components/schemas/UnknownToolConfig"}],"discriminator":{"propertyName":"type","mapping":{"algolia_display_results":"#/components/schemas/AlgoliaDisplayResultsToolConfig","algolia_recommend":"#/components/schemas/AlgoliaRecommendToolConfig-Output","algolia_search_index":"#/components/schemas/AlgoliaSearchToolConfig-Output","client_side":"#/components/schemas/ClientSideToolConfig","mcp_tools":"#/components/schemas/McpServerToolConfig-Output","unknown":"#/components/schemas/UnknownToolConfig"}}},"type":"array","title":"Tools"},"templateType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Templatetype"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updatedat"},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastusedat"}},"type":"object","required":["id","name","description","status","providerId","instructions","config","createdAt","updatedAt","lastUsedAt"],"title":"AgentWithVersionResponse"},"AlgoliaDisplayResultsToolConfig":{"properties":{"name":{"type":"string","const":"algolia_display_results","title":"Name","default":"algolia_display_results"},"type":{"type":"string","const":"algolia_display_results","title":"Type","default":"algolia_display_results"},"minGroups":{"type":"integer","minimum":1.0,"title":"Mingroups","default":1},"maxGroups":{"type":"integer","minimum":1.0,"title":"Maxgroups","default":3},"minResultsPerGroup":{"type":"integer","minimum":1.0,"title":"Minresultspergroup","default":3},"maxResultsPerGroup":{"type":"integer","minimum":1.0,"title":"Maxresultspergroup","default":6}},"type":"object","title":"AlgoliaDisplayResultsToolConfig","description":"Configuration for the algolia_display_results tool."},"AlgoliaRecommendToolConfig-Input":{"properties":{"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"algolia_recommend","title":"Type","default":"algolia_recommend"},"allowedConfigs":{"items":{"$ref":"#/components/schemas/AlgoliaRecommendToolIndexConfig"},"type":"array","title":"Allowedconfigs","default":[]},"predefinedRecommendParameters":{"additionalProperties":true,"type":"object","title":"Predefinedrecommendparameters"}},"type":"object","required":["name"],"title":"AlgoliaRecommendToolConfig","description":"Configuration for the Algolia Recommend tool.\nAllows specifying recommend models and related parameters."},"AlgoliaRecommendToolConfig-Output":{"properties":{"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"algolia_recommend","title":"Type","default":"algolia_recommend"},"allowedConfigs":{"items":{"$ref":"#/components/schemas/AlgoliaRecommendToolIndexConfig"},"type":"array","title":"Allowedconfigs","default":[]},"predefinedRecommendParameters":{"additionalProperties":true,"type":"object","title":"Predefinedrecommendparameters"},"description":{"type":"string","title":"Description","readOnly":true}},"type":"object","required":["name","description"],"title":"AlgoliaRecommendToolConfig","description":"Configuration for the Algolia Recommend tool.\nAllows specifying recommend models and related parameters."},"AlgoliaRecommendToolIndexConfig":{"properties":{"index":{"type":"string","maxLength":100,"minLength":1,"title":"Index"},"modelName":{"type":"string","maxLength":100,"minLength":1,"title":"Modelname"},"description":{"type":"string","title":"Description","default":""}},"type":"object","required":["index","modelName"],"title":"AlgoliaRecommendToolIndexConfig"},"AlgoliaSearchToolConfig-Input":{"properties":{"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"algolia_search_index","title":"Type","default":"algolia_search_index"},"indices":{"items":{"$ref":"#/components/schemas/AlgoliaSearchToolIndexConfig-Input"},"type":"array","title":"Indices"}},"type":"object","required":["name","indices"],"title":"AlgoliaSearchToolConfig"},"AlgoliaSearchToolConfig-Output":{"properties":{"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"algolia_search_index","title":"Type","default":"algolia_search_index"},"indices":{"items":{"$ref":"#/components/schemas/AlgoliaSearchToolIndexConfig-Output"},"type":"array","title":"Indices"},"description":{"type":"string","title":"Description","readOnly":true}},"type":"object","required":["name","indices","description"],"title":"AlgoliaSearchToolConfig"},"AlgoliaSearchToolIndexConfig-Input":{"properties":{"index":{"type":"string","maxLength":100,"minLength":1,"title":"Index"},"description":{"type":"string","maxLength":3000,"minLength":1,"title":"Description"},"enhancedDescription":{"type":"string","title":"Enhanceddescription","default":""},"searchParameters":{"anyOf":[{"$ref":"#/components/schemas/SearchParameters-Input"},{"type":"null"}]},"searchControls":{"anyOf":[{"$ref":"#/components/schemas/IndexSearchParameters-Input"},{"type":"null"}]}},"type":"object","required":["index","description"],"title":"AlgoliaSearchToolIndexConfig"},"AlgoliaSearchToolIndexConfig-Output":{"properties":{"index":{"type":"string","maxLength":100,"minLength":1,"title":"Index"},"description":{"type":"string","maxLength":3000,"minLength":1,"title":"Description"},"enhancedDescription":{"type":"string","title":"Enhanceddescription","default":""},"searchParameters":{"anyOf":[{"$ref":"#/components/schemas/SearchParameters-Output"},{"type":"null"}]},"searchControls":{"anyOf":[{"$ref":"#/components/schemas/IndexSearchParameters-Output"},{"type":"null"}]}},"type":"object","required":["index","description"],"title":"AlgoliaSearchToolIndexConfig"},"AllowedDomainBulkDelete":{"properties":{"domainIds":{"items":{"type":"string"},"type":"array","title":"Domainids","description":"IDs of allowed domain records to delete"}},"type":"object","required":["domainIds"],"title":"AllowedDomainBulkDelete","description":"Request body for bulk delete by IDs."},"AllowedDomainBulkInsert":{"properties":{"domains":{"items":{"type":"string"},"type":"array","title":"Domains","description":"List of domain patterns to add"}},"type":"object","required":["domains"],"title":"AllowedDomainBulkInsert","description":"Request body for bulk insert."},"AllowedDomainCreate":{"properties":{"domain":{"type":"string","title":"Domain","description":"Domain or pattern, e.g. https://app.example.com or *.example.com"}},"type":"object","required":["domain"],"title":"AllowedDomainCreate","description":"Request body to add a single allowed domain."},"AllowedDomainListResponse":{"properties":{"domains":{"items":{"$ref":"#/components/schemas/AllowedDomainResponse"},"type":"array","title":"Domains"}},"type":"object","required":["domains"],"title":"AllowedDomainListResponse","description":"List of allowed domains for an application."},"AllowedDomainResponse":{"properties":{"id":{"type":"string","title":"Id"},"appId":{"type":"string","title":"Appid"},"agentId":{"type":"string","title":"Agentid"},"domain":{"type":"string","title":"Domain"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"}},"type":"object","required":["id","appId","agentId","domain","createdAt","updatedAt"],"title":"AllowedDomainResponse","description":"Single allowed domain in API responses."},"AlternativesAsExact":{"type":"string","enum":["ignorePlurals","singleWordSynonym","multiWordsSynonym","ignoreConjugations"],"title":"AlternativesAsExact","description":"AlternativesAsExact"},"AnthropicProviderInput-Input":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"baseUrl":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Baseurl"}},"type":"object","required":["apiKey"],"title":"AnthropicProviderInput","description":"Anthropic-specific provider input."},"AnthropicProviderInput-Output":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"baseUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Baseurl"}},"type":"object","required":["apiKey"],"title":"AnthropicProviderInput","description":"Anthropic-specific provider input."},"ApplicationConfigPatch":{"properties":{"maxRetentionDays":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxretentiondays","description":"Maximum number of days to retain data. Valid values: [0, 30, 60, 90].","default":90}},"type":"object","title":"ApplicationConfigPatch"},"ApplicationConfigResponse":{"properties":{"maxRetentionDays":{"type":"integer","title":"Maxretentiondays"}},"type":"object","required":["maxRetentionDays"],"title":"ApplicationConfigResponse"},"AssistantMessageV4":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"role":{"type":"string","const":"assistant","title":"Role","default":"assistant"},"content":{"type":"string","title":"Content"},"parts":{"items":{"anyOf":[{"$ref":"#/components/schemas/StepStartPartV4"},{"$ref":"#/components/schemas/ReasoningPartV4"},{"$ref":"#/components/schemas/TextPartV4"},{"$ref":"#/components/schemas/ToolInvocationPartV4"}]},"type":"array","title":"Parts"},"toolInvocations":{"anyOf":[{"items":{"$ref":"#/components/schemas/ToolInvocationV4"},"type":"array"},{"type":"null"}],"title":"Toolinvocations"}},"type":"object","required":["content"],"title":"AssistantMessageV4"},"AssistantMessageV5":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"role":{"type":"string","const":"assistant","title":"Role","default":"assistant"},"parts":{"items":{"anyOf":[{"$ref":"#/components/schemas/StepStartPartV5"},{"$ref":"#/components/schemas/TextPartV5"},{"$ref":"#/components/schemas/ReasoningPartV5"},{"$ref":"#/components/schemas/ToolPartV5"}]},"type":"array","title":"Parts"}},"type":"object","title":"AssistantMessageV5"},"AzureOpenAIProviderInput-Input":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"azureEndpoint":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Azureendpoint"},"azureDeployment":{"type":"string","minLength":1,"title":"Azuredeployment","description":"Azure model deployment name is required"},"apiVersion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Apiversion","default":"2024-12-01-preview"}},"type":"object","required":["apiKey","azureEndpoint","azureDeployment"],"title":"AzureOpenAIProviderInput","description":"Azure OpenAI-specific provider input."},"AzureOpenAIProviderInput-Output":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"azureEndpoint":{"type":"string","title":"Azureendpoint"},"azureDeployment":{"type":"string","minLength":1,"title":"Azuredeployment","description":"Azure model deployment name is required"},"apiVersion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Apiversion","default":"2024-12-01-preview"}},"type":"object","required":["apiKey","azureEndpoint","azureDeployment"],"title":"AzureOpenAIProviderInput","description":"Azure OpenAI-specific provider input."},"BaseProviderInput":{"properties":{"apiKey":{"type":"string","title":"Apikey"}},"type":"object","required":["apiKey"],"title":"BaseProviderInput","description":"Base input that all providers must have."},"ClientSideToolConfig":{"properties":{"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"client_side","title":"Type","default":"client_side"},"description":{"type":"string","maxLength":200,"minLength":1,"title":"Description"},"inputSchema":{"$ref":"#/components/schemas/ClientToolsArgsSchema"}},"type":"object","required":["name","description","inputSchema"],"title":"ClientSideToolConfig"},"ClientToolsArgsSchema":{"properties":{"type":{"type":"string","const":"object","title":"Type","default":"object"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"},"required":{"items":{"type":"string"},"type":"array","title":"Required"}},"type":"object","title":"ClientToolsArgsSchema"},"CompatibilityMode":{"type":"string","enum":["ai-sdk-4","ai-sdk-5"],"title":"CompatibilityMode","description":"Support Compatibility modes for the completion API."},"ConversationBaseResponse":{"properties":{"id":{"type":"string","title":"Id"},"agentId":{"type":"string","format":"uuid","title":"Agentid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"lastActivityAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastactivityat"},"userToken":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usertoken"},"isFromDashboard":{"type":"boolean","title":"Isfromdashboard","default":false},"messageCount":{"type":"integer","title":"Messagecount","default":0},"totalInputTokens":{"type":"integer","title":"Totalinputtokens","default":0},"totalOutputTokens":{"type":"integer","title":"Totaloutputtokens","default":0},"totalTokens":{"type":"integer","title":"Totaltokens","default":0},"conversationMetadata":{"anyOf":[{"$ref":"#/components/schemas/ConversationMetadata"},{"type":"null"}]},"feedback":{"anyOf":[{"items":{"$ref":"#/components/schemas/FeedbackResponse"},"type":"array"},{"type":"null"}],"title":"Feedback"}},"type":"object","required":["id","agentId","createdAt","updatedAt"],"title":"ConversationBaseResponse","description":"Lightweight response model without its messages."},"ConversationFullResponse":{"properties":{"id":{"type":"string","title":"Id"},"agentId":{"type":"string","format":"uuid","title":"Agentid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"lastActivityAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastactivityat"},"userToken":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usertoken"},"isFromDashboard":{"type":"boolean","title":"Isfromdashboard","default":false},"messageCount":{"type":"integer","title":"Messagecount","default":0},"totalInputTokens":{"type":"integer","title":"Totalinputtokens","default":0},"totalOutputTokens":{"type":"integer","title":"Totaloutputtokens","default":0},"totalTokens":{"type":"integer","title":"Totaltokens","default":0},"conversationMetadata":{"anyOf":[{"$ref":"#/components/schemas/ConversationMetadata"},{"type":"null"}]},"feedback":{"anyOf":[{"items":{"$ref":"#/components/schemas/FeedbackResponse"},"type":"array"},{"type":"null"}],"title":"Feedback"},"messages":{"items":{"$ref":"#/components/schemas/MessageResponse-Output"},"type":"array","title":"Messages"}},"type":"object","required":["id","agentId","createdAt","updatedAt","messages"],"title":"ConversationFullResponse","description":"Response model for a conversation with all its messages."},"ConversationMetadata":{"properties":{"cachedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cachedat"}},"type":"object","title":"ConversationMetadata","description":"Public metadata exposed on conversation responses."},"Episode":{"properties":{"observation":{"type":"string","maxLength":5000,"minLength":1,"title":"Observation","description":"What user wanted + key context (1-2 sentences). Include prior failed attempts if they informed the approach."},"thoughts":{"type":"string","maxLength":5000,"minLength":1,"title":"Thoughts","description":"WHY this approach was chosen, which constraints/preferences drove decisions (1-3 sentences). Capture reasoning that applies to similar future scenarios."},"action":{"type":"string","maxLength":5000,"minLength":1,"title":"Action","description":"What was done with PRECISE details (1-3 sentences). WITH tool calls: use arrow notation `tool(param:value) → feedback → tool(refined_param:new_value)`. WITHOUT tool calls: capture communication/workflow pattern."},"result":{"type":"string","maxLength":5000,"minLength":1,"title":"Result","description":"Learned pattern + effectiveness (1-3 sentences). What worked and WHY it's replicable. Note efficiency: multi-turn refinements, which results were relevant, what made final attempt succeed. Use strict `param:value` syntax for learnings. Format: 'For [context], use [param:value] because [reason]'."}},"type":"object","required":["observation","thoughts","action","result"],"title":"Episode","description":"Episodic memory schema following LangMem's OTAR pattern:\nObservation → Thoughts → Action → Result\n\nCaptures complete interaction experiences for agent learning.\nSee https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#episodic-memory-past-experiences"},"ExactOnSingleWordQuery":{"type":"string","enum":["attribute","none","word"],"title":"ExactOnSingleWordQuery","description":"Determines how the [Exact ranking criterion](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/adjust-exact-settings/#turn-off-exact-for-some-attributes) is computed when the search query has only one word. - `attribute`. The Exact ranking criterion is 1 if the query word and attribute value are the same. For example, a search for \"road\" will match the value \"road\", but not \"road trip\". - `none`. The Exact ranking criterion is ignored on single-word searches. - `word`. The Exact ranking criterion is 1 if the query word is found in the attribute value. The query word must have at least 3 characters and must not be a stop word. Only exact matches will be highlighted, partial and prefix matches won't."},"FacetFilters-Input":{"properties":{"oneof_schema_1_validator":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacetFilters-Input"},"type":"array"},{"type":"null"}],"title":"Oneof Schema 1 Validator"},"oneof_schema_2_validator":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Oneof Schema 2 Validator"},"actual_instance":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacetFilters-Input"},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Actual Instance"},"one_of_schemas":{"items":{"type":"string"},"type":"array","uniqueItems":true,"title":"One Of Schemas","default":["str","List[FacetFilters]"]}},"type":"object","title":"FacetFilters","description":"Filter the search by facet values, so that only records with the same facet values are retrieved. **Prefer using the `filters` parameter, which supports all filter types and combinations with boolean operators.** - `[filter1, filter2]` is interpreted as `filter1 AND filter2`. - `[[filter1, filter2], filter3]` is interpreted as `filter1 OR filter2 AND filter3`. - `facet:-value` is interpreted as `NOT facet:value`. While it's best to avoid attributes that start with a `-`, you can still filter them by escaping with a backslash: `facet:\\-value`."},"FacetFilters-Output":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacetFilters-Output"},"type":"array"},{"type":"string"},{"$ref":"#/components/schemas/FacetFilters-Output"},{"type":"null"}]},"Facets":{"properties":{"order":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Order"}},"additionalProperties":true,"type":"object","title":"Facets","description":"Order of facet names."},"FacetsParam":{"properties":{"exposed":{"type":"boolean","const":false,"title":"Exposed","default":false},"default":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Default"}},"type":"object","title":"FacetsParam","description":"A facets parameter that is always hidden from the LLM."},"FeedbackCreationRequest":{"properties":{"messageId":{"type":"string","title":"Messageid"},"agentId":{"type":"string","format":"uuid","title":"Agentid"},"vote":{"type":"integer","enum":[0,1],"title":"Vote"},"tags":{"anyOf":[{"items":{"type":"string","maxLength":50},"type":"array","maxItems":10},{"type":"null"}],"title":"Tags"},"notes":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Notes"}},"type":"object","required":["messageId","agentId","vote"],"title":"FeedbackCreationRequest","description":"Request model for creating a feedback entry."},"FeedbackResponse":{"properties":{"id":{"type":"string","title":"Id"},"agentId":{"type":"string","format":"uuid","title":"Agentid"},"messageId":{"type":"string","title":"Messageid"},"vote":{"type":"integer","title":"Vote"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"}},"type":"object","required":["id","agentId","messageId","vote","tags","createdAt","updatedAt"],"title":"FeedbackResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IndexSearchParameters-Input":{"properties":{"query":{"anyOf":[{"$ref":"#/components/schemas/TextParam"},{"type":"null"}]},"hitsPerPage":{"anyOf":[{"$ref":"#/components/schemas/NumberParam"},{"type":"null"}]},"page":{"anyOf":[{"$ref":"#/components/schemas/NumberParam"},{"type":"null"}]},"attributesToRetrieve":{"anyOf":[{"$ref":"#/components/schemas/StringArrayParam"},{"type":"null"}]},"responseFields":{"anyOf":[{"$ref":"#/components/schemas/StringArrayParam"},{"type":"null"}]},"facets":{"anyOf":[{"$ref":"#/components/schemas/FacetsParam"},{"type":"null"}]},"custom":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom"}},"type":"object","title":"IndexSearchParameters","description":"Structured search parameters configuration for an Algolia index.\n\nEach parameter controls whether it is exposed to the LLM, its default value,\noptional constraints, and merge behavior."},"IndexSearchParameters-Output":{"properties":{"query":{"anyOf":[{"$ref":"#/components/schemas/TextParam"},{"type":"null"}]},"hitsPerPage":{"anyOf":[{"$ref":"#/components/schemas/NumberParam"},{"type":"null"}]},"page":{"anyOf":[{"$ref":"#/components/schemas/NumberParam"},{"type":"null"}]},"attributesToRetrieve":{"anyOf":[{"$ref":"#/components/schemas/StringArrayParam"},{"type":"null"}]},"responseFields":{"anyOf":[{"$ref":"#/components/schemas/StringArrayParam"},{"type":"null"}]},"facets":{"anyOf":[{"$ref":"#/components/schemas/FacetsParam"},{"type":"null"}]},"custom":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom"}},"type":"object","title":"IndexSearchParameters","description":"Structured search parameters configuration for an Algolia index.\n\nEach parameter controls whether it is exposed to the LLM, its default value,\noptional constraints, and merge behavior."},"McpServerToolConfig-Input":{"properties":{"url":{"type":"string","maxLength":512,"minLength":1,"title":"Url"},"transport":{"type":"string","const":"streamable_http","title":"Transport","default":"streamable_http"},"headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Headers"},"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"mcp_tools","title":"Type","default":"mcp_tools"},"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id","description":"Stable unique identifier for this MCP tool"},"allowedTools":{"anyOf":[{"additionalProperties":{"anyOf":[{"$ref":"#/components/schemas/McpToolConfig"},{"type":"boolean"}]},"type":"object"},{"type":"null"}],"title":"Allowedtools"}},"type":"object","required":["url","headers","name"],"title":"McpServerToolConfig"},"McpServerToolConfig-Output":{"properties":{"url":{"type":"string","maxLength":512,"minLength":1,"title":"Url"},"transport":{"type":"string","const":"streamable_http","title":"Transport","default":"streamable_http"},"headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Headers"},"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"mcp_tools","title":"Type","default":"mcp_tools"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Stable unique identifier for this MCP tool"},"allowedTools":{"anyOf":[{"additionalProperties":{"anyOf":[{"$ref":"#/components/schemas/McpToolConfig"},{"type":"boolean"}]},"type":"object"},{"type":"null"}],"title":"Allowedtools"}},"type":"object","required":["url","headers","name"],"title":"McpServerToolConfig"},"McpToolConfig":{"properties":{"requiresApproval":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Requiresapproval","default":false},"alias":{"anyOf":[{"type":"string","maxLength":32,"minLength":3},{"type":"null"}],"title":"Alias"}},"type":"object","title":"McpToolConfig"},"MemoryRecord":{"properties":{"memoryType":{"$ref":"#/components/schemas/MemoryType","description":"Memory type: semantic (facts) or episodic (experiences)","default":"semantic"},"episode":{"anyOf":[{"$ref":"#/components/schemas/Episode"},{"type":"null"}],"description":"Episode data for episodic memories (observation → thoughts → action → result)"},"text":{"type":"string","maxLength":2000,"minLength":1,"title":"Text","description":"Self-contained, first-person memory for long-term recall"},"rawExtract":{"type":"string","maxLength":5000,"minLength":1,"title":"Rawextract","description":"Verbatim conversation extract, not paraphrased"},"keywords":{"items":{"type":"string"},"type":"array","title":"Keywords","description":"5-20 free-form keywords: entities, context, search terms (any words)"},"topics":{"items":{"type":"string"},"type":"array","title":"Topics","description":"2-4 topics ONLY from this list: [complaints, entertainment, family, feedback, finance, food, goals, health, history, hobbies, learning, praise, preferences, schedule, shopping, technical, travel, work]"},"_tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"Arbitrary labels/themes for flexible categorization (e.g., 'Q1-goals', 'paris-trip', 'vip-customer')"},"recallTriggers":{"items":{"type":"string"},"type":"array","title":"Recalltriggers","description":"3-5 natural phrases that should trigger this memory"},"objectID":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Objectid","description":"ObjectID of existing memory to update. Leave empty for new memory."},"appId":{"type":"string","title":"Appid","description":"Application ID","default":""},"agentIDs":{"items":{"type":"string"},"type":"array","title":"Agentids","description":"Agent IDs with access: ['agent1'], ['*'] for all, ['*', '-agent1'] to exclude"},"userID":{"type":"string","title":"Userid","description":"User ID","default":""},"createdAt":{"type":"integer","title":"Createdat","description":"Epoch seconds","default":0},"updatedAt":{"type":"integer","title":"Updatedat","description":"Epoch seconds","default":0}},"type":"object","required":["text","rawExtract"],"title":"MemoryRecord","description":"Universal storage model for all memory types (semantic, episodic).\n\nThis is the ONLY model that touches storage (Algolia). Domain models (SemanticMemory, EpisodicMemory)\nare used for LLM extraction and converted to MemoryRecord before saving.\n\nSee https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#memory-types for memory type definitions."},"MemoryType":{"type":"string","enum":["semantic","episodic"],"title":"MemoryType","description":"Memory types implemented so far.\nFollows LangMem's ontology: https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#memory-types"},"MessageResponse-Input":{"properties":{"id":{"type":"string","title":"Id"},"conversationId":{"type":"string","title":"Conversationid"},"role":{"$ref":"#/components/schemas/MessageRole"},"parts":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextPart"},{"$ref":"#/components/schemas/ToolCallPart"},{"$ref":"#/components/schemas/ToolResultPart-Input"},{"$ref":"#/components/schemas/StartPart"},{"$ref":"#/components/schemas/StartStepPart"},{"$ref":"#/components/schemas/ReasoningPart"},{"$ref":"#/components/schemas/ToolApprovalRequestPart-Input"}],"discriminator":{"propertyName":"type","mapping":{"reasoning":"#/components/schemas/ReasoningPart","start":"#/components/schemas/StartPart","start-step":"#/components/schemas/StartStepPart","text":"#/components/schemas/TextPart","tool-approval-request":"#/components/schemas/ToolApprovalRequestPart-Input","tool-call":"#/components/schemas/ToolCallPart","tool-result":"#/components/schemas/ToolResultPart-Input"}}},"type":"array","title":"Parts"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"inputTokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Inputtokens"},"outputTokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Outputtokens"}},"type":"object","required":["id","conversationId","role","parts","createdAt","updatedAt"],"title":"MessageResponse","description":"Response model for a message."},"MessageResponse-Output":{"properties":{"id":{"type":"string","title":"Id"},"conversationId":{"type":"string","title":"Conversationid"},"role":{"$ref":"#/components/schemas/MessageRole"},"parts":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextPart"},{"$ref":"#/components/schemas/ToolCallPart"},{"$ref":"#/components/schemas/ToolResultPart-Output"},{"$ref":"#/components/schemas/StartPart"},{"$ref":"#/components/schemas/StartStepPart"},{"$ref":"#/components/schemas/ReasoningPart"},{"$ref":"#/components/schemas/ToolApprovalRequestPart-Output"}],"discriminator":{"propertyName":"type","mapping":{"reasoning":"#/components/schemas/ReasoningPart","start":"#/components/schemas/StartPart","start-step":"#/components/schemas/StartStepPart","text":"#/components/schemas/TextPart","tool-approval-request":"#/components/schemas/ToolApprovalRequestPart-Output","tool-call":"#/components/schemas/ToolCallPart","tool-result":"#/components/schemas/ToolResultPart-Output"}}},"type":"array","title":"Parts"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"inputTokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Inputtokens"},"outputTokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Outputtokens"}},"type":"object","required":["id","conversationId","role","parts","createdAt","updatedAt"],"title":"MessageResponse","description":"Response model for a message."},"MessageRole":{"type":"string","enum":["user","assistant"],"title":"MessageRole","description":"Role of a message in the conversation."},"NumberParam":{"properties":{"exposed":{"type":"boolean","title":"Exposed"},"default":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Default"},"constraint":{"anyOf":[{"$ref":"#/components/schemas/NumberParamConstraint"},{"type":"null"}]}},"type":"object","required":["exposed"],"title":"NumberParam","description":"A number search parameter with exposure control and optional constraints."},"NumberParamConstraint":{"properties":{"min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min"},"max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max"}},"type":"object","title":"NumberParamConstraint","description":"Constraints for a number parameter."},"OpenAICompatibleProviderInput-Input":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"baseUrl":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Baseurl"},"defaultModel":{"type":"string","minLength":1,"title":"Defaultmodel","description":"Default model for this provider. Used for validation and as fallback when no model is specified at agent level."}},"type":"object","required":["apiKey","baseUrl","defaultModel"],"title":"OpenAICompatibleProviderInput","description":"OpenAI-compatible provider input.\nContrary to the OpenAIProviderInput, the base_url is required.\nA model is required to verify connectivity and get saved as the default model.\nThis can later be changed at the Agent level."},"OpenAICompatibleProviderInput-Output":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"baseUrl":{"type":"string","title":"Baseurl"},"defaultModel":{"type":"string","minLength":1,"title":"Defaultmodel","description":"Default model for this provider. Used for validation and as fallback when no model is specified at agent level."}},"type":"object","required":["apiKey","baseUrl","defaultModel"],"title":"OpenAICompatibleProviderInput","description":"OpenAI-compatible provider input.\nContrary to the OpenAIProviderInput, the base_url is required.\nA model is required to verify connectivity and get saved as the default model.\nThis can later be changed at the Agent level."},"OpenAIProviderInput-Input":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"baseUrl":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Baseurl"}},"type":"object","required":["apiKey"],"title":"OpenAIProviderInput","description":"OpenAI-specific provider input."},"OpenAIProviderInput-Output":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"baseUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Baseurl"}},"type":"object","required":["apiKey"],"title":"OpenAIProviderInput","description":"OpenAI-specific provider input."},"PaginatedAgentsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AgentWithVersionResponse"},"type":"array","title":"Data"},"pagination":{"$ref":"#/components/schemas/PaginationMetadata"}},"type":"object","required":["data","pagination"],"title":"PaginatedAgentsResponse"},"PaginatedConversationsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ConversationBaseResponse"},"type":"array","title":"Data"},"pagination":{"$ref":"#/components/schemas/PaginationMetadata"}},"type":"object","required":["data","pagination"],"title":"PaginatedConversationsResponse"},"PaginatedProviderAuthenticationsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ProviderAuthenticationResponse"},"type":"array","title":"Data"},"pagination":{"$ref":"#/components/schemas/PaginationMetadata"}},"type":"object","required":["data","pagination"],"title":"PaginatedProviders"},"PaginatedSecretKeysResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/SecretKeyResponse"},"type":"array","title":"Data"},"pagination":{"$ref":"#/components/schemas/PaginationMetadata"}},"type":"object","required":["data","pagination"],"title":"PaginatedSecretKeysResponse"},"PaginationMetadata":{"properties":{"page":{"type":"integer","title":"Page"},"limit":{"type":"integer","title":"Limit"},"totalCount":{"type":"integer","title":"Totalcount"},"totalPages":{"type":"integer","title":"Totalpages"}},"type":"object","required":["page","limit","totalCount","totalPages"],"title":"PaginationMetadata"},"ProviderAuthenticationCreate":{"properties":{"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name"},"providerName":{"$ref":"#/components/schemas/ProviderName"},"input":{"anyOf":[{"$ref":"#/components/schemas/OpenAIProviderInput-Input"},{"$ref":"#/components/schemas/AzureOpenAIProviderInput-Input"},{"$ref":"#/components/schemas/OpenAICompatibleProviderInput-Input"},{"$ref":"#/components/schemas/BaseProviderInput"},{"$ref":"#/components/schemas/AnthropicProviderInput-Input"}],"title":"Input"}},"additionalProperties":false,"type":"object","required":["name","providerName","input"],"title":"ProviderAuthenticationCreate"},"ProviderAuthenticationPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Name"},"input":{"anyOf":[{"$ref":"#/components/schemas/OpenAIProviderInput-Input"},{"$ref":"#/components/schemas/AzureOpenAIProviderInput-Input"},{"$ref":"#/components/schemas/OpenAICompatibleProviderInput-Input"},{"$ref":"#/components/schemas/BaseProviderInput"},{"$ref":"#/components/schemas/AnthropicProviderInput-Input"},{"type":"null"}],"title":"Input"}},"additionalProperties":false,"type":"object","title":"ProviderPatch"},"ProviderAuthenticationResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"providerName":{"type":"string","title":"Providername"},"input":{"anyOf":[{"$ref":"#/components/schemas/OpenAIProviderInput-Output"},{"$ref":"#/components/schemas/AzureOpenAIProviderInput-Output"},{"$ref":"#/components/schemas/OpenAICompatibleProviderInput-Output"},{"$ref":"#/components/schemas/BaseProviderInput"},{"$ref":"#/components/schemas/AnthropicProviderInput-Output"}],"title":"Input"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastusedat"}},"type":"object","required":["id","name","providerName","input","createdAt","updatedAt"],"title":"Provider"},"ProviderName":{"type":"string","enum":["openai","azure_openai","google_genai","deepseek","openai_compatible","anthropic"],"title":"ProviderName"},"QueryType":{"type":"string","enum":["prefixLast","prefixAll","prefixNone"],"title":"QueryType","description":"Determines if and how query words are interpreted as prefixes. By default, only the last query word is treated as a prefix (`prefixLast`). To turn off prefix search, use `prefixNone`. Avoid `prefixAll`, which treats all query words as prefixes. This might lead to counterintuitive results and makes your search slower. For more information, see [Prefix searching](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/prefix-searching)."},"ReasoningPart":{"properties":{"type":{"type":"string","const":"reasoning","title":"Type","default":"reasoning"},"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"ReasoningPart"},"ReasoningPartV4":{"properties":{"type":{"type":"string","const":"reasoning","title":"Type","default":"reasoning"},"reasoning":{"type":"string","title":"Reasoning"}},"type":"object","required":["reasoning"],"title":"ReasoningPartV4"},"ReasoningPartV5":{"properties":{"type":{"type":"string","const":"reasoning","title":"Type","default":"reasoning"},"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"ReasoningPartV5"},"RemoveWordsIfNoResults":{"type":"string","enum":["none","lastWords","firstWords","allOptional"],"title":"RemoveWordsIfNoResults","description":"Strategy for removing words from the query when it doesn't return any results. This helps to avoid returning empty search results. - `none`. No words are removed when a query doesn't return results. - `lastWords`. Treat the last (then second to last, then third to last) word as optional, until there are results or at most 5 words have been removed. - `firstWords`. Treat the first (then second, then third) word as optional, until there are results or at most 5 words have been removed. - `allOptional`. Treat all words as optional. For more information, see [Remove words to improve results](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/empty-or-insufficient-results/in-depth/why-use-remove-words-if-no-results)."},"SearchParameters-Input":{"properties":{"queryType":{"anyOf":[{"$ref":"#/components/schemas/QueryType"},{"type":"null"}]},"similarQuery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Similarquery"},"queryLanguages":{"anyOf":[{"items":{"$ref":"#/components/schemas/SupportedLanguage"},"type":"array"},{"type":"null"}],"title":"Querylanguages"},"advancedSyntax":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Advancedsyntax"},"advancedSyntaxFeatures":{"anyOf":[{"items":{"$ref":"#/components/schemas/AdvancedSyntaxFeatures"},"type":"array"},{"type":"null"}],"title":"Advancedsyntaxfeatures"},"alternativesAsExact":{"anyOf":[{"items":{"$ref":"#/components/schemas/AlternativesAsExact"},"type":"array"},{"type":"null"}],"title":"Alternativesasexact"},"decompoundQuery":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Decompoundquery"},"typoTolerance":{"anyOf":[{"type":"boolean"},{"$ref":"#/components/schemas/TypoToleranceEnum"},{"type":"null"}],"title":"Typotolerance"},"allowTyposOnNumericTokens":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allowtyposonnumerictokens"},"minWordSizeFor1Typo":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minwordsizefor1Typo"},"minWordSizeFor2Typos":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minwordsizefor2Typos"},"disableTypoToleranceOnAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Disabletypotoleranceonattributes"},"filters":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filters"},"facetFilters":{"anyOf":[{"$ref":"#/components/schemas/FacetFilters-Input"},{"type":"null"}]},"facets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"$ref":"#/components/schemas/Facets"},{"type":"null"}],"title":"Facets"},"maxValuesPerFacet":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxvaluesperfacet"},"maxFacetHits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxfacethits"},"facetingAfterDistinct":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Facetingafterdistinct"},"sortFacetValuesBy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sortfacetvaluesby"},"numericFilters":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Numericfilters"},"tagFilters":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Tagfilters"},"sumOrFiltersScores":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Sumorfiltersscores"},"aroundLatLng":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Aroundlatlng"},"aroundLatLngViaIp":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Aroundlatlngviaip"},"aroundRadius":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Aroundradius"},"aroundPrecision":{"anyOf":[{"type":"integer"},{"items":{"additionalProperties":{"type":"integer"},"type":"object"},"type":"array"},{"type":"null"}],"title":"Aroundprecision"},"minimumAroundRadius":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minimumaroundradius"},"insideBoundingBox":{"anyOf":[{"type":"string"},{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Insideboundingbox"},"insidePolygon":{"anyOf":[{"type":"string"},{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Insidepolygon"},"attributesToRetrieve":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Attributestoretrieve"},"attributesToSnippet":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Attributestosnippet"},"snippetEllipsisText":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Snippetellipsistext"},"restrictHighlightAndSnippetArrays":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Restricthighlightandsnippetarrays"},"page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"},"offset":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Offset"},"hitsPerPage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hitsperpage"},"length":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Length"},"getRankingInfo":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Getrankinginfo"},"relevancyStrictness":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Relevancystrictness"},"minProximity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minproximity"},"attributeCriteriaComputedByMinProximity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Attributecriteriacomputedbyminproximity"},"distinct":{"anyOf":[{"type":"boolean"},{"type":"integer"},{"type":"null"}],"title":"Distinct"},"enableRules":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablerules"},"enablePersonalization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablepersonalization"},"personalizationImpact":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Personalizationimpact"},"enableAbTest":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enableabtest"},"enableReRanking":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablereranking"},"reRankingApplyFilter":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Rerankingapplyfilter"},"ruleContexts":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Rulecontexts"},"removeStopWords":{"anyOf":[{"type":"boolean"},{"items":{"$ref":"#/components/schemas/SupportedLanguage"},"type":"array"},{"type":"null"}],"title":"Removestopwords"},"ignorePlurals":{"anyOf":[{"type":"boolean"},{"items":{"$ref":"#/components/schemas/SupportedLanguage"},"type":"array"},{"type":"null"}],"title":"Ignoreplurals"},"removeWordsIfNoResults":{"anyOf":[{"$ref":"#/components/schemas/RemoveWordsIfNoResults"},{"type":"null"}]},"optionalWords":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Optionalwords"},"optionalFilters":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Optionalfilters"},"synonyms":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Synonyms"},"replaceSynonymsInHighlight":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Replacesynonymsinhighlight"},"analytics":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Analytics"},"analyticsTags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Analyticstags"},"clickAnalytics":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Clickanalytics"},"userToken":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usertoken"},"restrictSearchableAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Restrictsearchableattributes"},"disableExactOnAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Disableexactonattributes"},"exactOnSingleWordQuery":{"anyOf":[{"$ref":"#/components/schemas/ExactOnSingleWordQuery"},{"type":"null"}]},"naturalLanguages":{"anyOf":[{"items":{"$ref":"#/components/schemas/SupportedLanguage"},"type":"array"},{"type":"null"}],"title":"Naturallanguages"},"percentileComputation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Percentilecomputation"},"explain":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Explain"}},"type":"object","title":"SearchParameters","description":"Algolia Search API parameters that can be predefined for the search tool.\nReference: https://www.algolia.com/doc/api-reference/search-api-parameters/\n\nThe parameters that seemed irrelevant for the search tool have been commented out.\nUses types from algoliasearch.search.models for better type safety."},"SearchParameters-Output":{"properties":{"queryType":{"anyOf":[{"$ref":"#/components/schemas/QueryType"},{"type":"null"}]},"similarQuery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Similarquery"},"queryLanguages":{"anyOf":[{"items":{"$ref":"#/components/schemas/SupportedLanguage"},"type":"array"},{"type":"null"}],"title":"Querylanguages"},"advancedSyntax":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Advancedsyntax"},"advancedSyntaxFeatures":{"anyOf":[{"items":{"$ref":"#/components/schemas/AdvancedSyntaxFeatures"},"type":"array"},{"type":"null"}],"title":"Advancedsyntaxfeatures"},"alternativesAsExact":{"anyOf":[{"items":{"$ref":"#/components/schemas/AlternativesAsExact"},"type":"array"},{"type":"null"}],"title":"Alternativesasexact"},"decompoundQuery":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Decompoundquery"},"typoTolerance":{"anyOf":[{"type":"boolean"},{"type":"string"},{"type":"null"}],"title":"Typotolerance"},"allowTyposOnNumericTokens":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allowtyposonnumerictokens"},"minWordSizeFor1Typo":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minwordsizefor1Typo"},"minWordSizeFor2Typos":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minwordsizefor2Typos"},"disableTypoToleranceOnAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Disabletypotoleranceonattributes"},"filters":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filters"},"facetFilters":{"anyOf":[{"$ref":"#/components/schemas/FacetFilters-Output"},{"type":"null"}]},"facets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"$ref":"#/components/schemas/Facets"},{"type":"null"}],"title":"Facets"},"maxValuesPerFacet":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxvaluesperfacet"},"maxFacetHits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxfacethits"},"facetingAfterDistinct":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Facetingafterdistinct"},"sortFacetValuesBy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sortfacetvaluesby"},"numericFilters":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Numericfilters"},"tagFilters":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Tagfilters"},"sumOrFiltersScores":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Sumorfiltersscores"},"aroundLatLng":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Aroundlatlng"},"aroundLatLngViaIp":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Aroundlatlngviaip"},"aroundRadius":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Aroundradius"},"aroundPrecision":{"anyOf":[{"type":"integer"},{"items":{"additionalProperties":{"type":"integer"},"type":"object"},"type":"array"},{"type":"null"}],"title":"Aroundprecision"},"minimumAroundRadius":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minimumaroundradius"},"insideBoundingBox":{"anyOf":[{"type":"string"},{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Insideboundingbox"},"insidePolygon":{"anyOf":[{"type":"string"},{"items":{"items":{"type":"number"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Insidepolygon"},"attributesToRetrieve":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Attributestoretrieve"},"attributesToSnippet":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Attributestosnippet"},"snippetEllipsisText":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Snippetellipsistext"},"restrictHighlightAndSnippetArrays":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Restricthighlightandsnippetarrays"},"page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"},"offset":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Offset"},"hitsPerPage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hitsperpage"},"length":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Length"},"getRankingInfo":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Getrankinginfo"},"relevancyStrictness":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Relevancystrictness"},"minProximity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minproximity"},"attributeCriteriaComputedByMinProximity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Attributecriteriacomputedbyminproximity"},"distinct":{"anyOf":[{"type":"boolean"},{"type":"integer"},{"type":"null"}],"title":"Distinct"},"enableRules":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablerules"},"enablePersonalization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablepersonalization"},"personalizationImpact":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Personalizationimpact"},"enableAbTest":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enableabtest"},"enableReRanking":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablereranking"},"reRankingApplyFilter":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Rerankingapplyfilter"},"ruleContexts":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Rulecontexts"},"removeStopWords":{"anyOf":[{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Removestopwords"},"ignorePlurals":{"anyOf":[{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Ignoreplurals"},"removeWordsIfNoResults":{"anyOf":[{"$ref":"#/components/schemas/RemoveWordsIfNoResults"},{"type":"null"}]},"optionalWords":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Optionalwords"},"optionalFilters":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Optionalfilters"},"synonyms":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Synonyms"},"replaceSynonymsInHighlight":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Replacesynonymsinhighlight"},"analytics":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Analytics"},"analyticsTags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Analyticstags"},"clickAnalytics":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Clickanalytics"},"userToken":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usertoken"},"restrictSearchableAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Restrictsearchableattributes"},"disableExactOnAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Disableexactonattributes"},"exactOnSingleWordQuery":{"anyOf":[{"$ref":"#/components/schemas/ExactOnSingleWordQuery"},{"type":"null"}]},"naturalLanguages":{"anyOf":[{"items":{"$ref":"#/components/schemas/SupportedLanguage"},"type":"array"},{"type":"null"}],"title":"Naturallanguages"},"percentileComputation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Percentilecomputation"},"explain":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Explain"}},"type":"object","title":"SearchParameters","description":"Algolia Search API parameters that can be predefined for the search tool.\nReference: https://www.algolia.com/doc/api-reference/search-api-parameters/\n\nThe parameters that seemed irrelevant for the search tool have been commented out.\nUses types from algoliasearch.search.models for better type safety."},"SearchParametersOverrides":{"properties":{"filters":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filters"},"attributesToRetrieve":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Attributestoretrieve"},"restrictSearchableAttributes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Restrictsearchableattributes"},"distinct":{"anyOf":[{"type":"boolean"},{"type":"integer"},{"type":"null"}],"title":"Distinct"},"userToken":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usertoken"},"enablePersonalization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enablepersonalization"},"personalizationImpact":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Personalizationimpact"}},"additionalProperties":false,"type":"object","title":"SearchParametersOverrides","description":"Algolia Search API parameters that can be predefined for the search tool.\nReference: https://www.algolia.com/doc/api-reference/search-api-parameters/\n\nA subset of SearchParameters of specific params we allow for runtime override."},"SecretKeyCreate":{"properties":{"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name","description":"The name of the secret key"},"agentIds":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Agentids","description":"List of agent IDs this secret key is associated with"}},"type":"object","required":["name"],"title":"SecretKeyCreate","description":"Secret key creation payload"},"SecretKeyPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Name","description":"The new name of the secret key"},"agentIds":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Agentids","description":"Updated list of agent IDs this secret key is associated with"}},"type":"object","title":"SecretKeyPatch","description":"Secret key patch payload"},"SecretKeyResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"value":{"type":"string","title":"Value"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastusedat"},"isDefault":{"type":"boolean","title":"Isdefault","default":false},"agentIds":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Agentids"}},"type":"object","required":["id","name","value","createdAt","updatedAt","lastUsedAt","agentIds"],"title":"SecretKeyResponse"},"StartPart":{"properties":{"type":{"type":"string","const":"start","title":"Type","default":"start"}},"type":"object","title":"StartPart"},"StartStepPart":{"properties":{"type":{"type":"string","const":"start-step","title":"Type","default":"start-step"}},"type":"object","title":"StartStepPart"},"StepStartPartV4":{"properties":{"type":{"type":"string","const":"step-start","title":"Type","default":"step-start"}},"type":"object","title":"StepStartPartV4"},"StepStartPartV5":{"properties":{"type":{"type":"string","const":"step-start","title":"Type","default":"step-start"}},"type":"object","title":"StepStartPartV5"},"StringArrayParam":{"properties":{"exposed":{"type":"boolean","title":"Exposed"},"default":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Default"},"constraint":{"anyOf":[{"$ref":"#/components/schemas/StringArrayParamConstraint"},{"type":"null"}]},"merge":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Merge"}},"type":"object","required":["exposed"],"title":"StringArrayParam","description":"A string array search parameter with exposure control, constraints, and merge behavior."},"StringArrayParamConstraint":{"properties":{"values":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Values"}},"type":"object","title":"StringArrayParamConstraint","description":"Constraints for a string array parameter."},"SupportedLanguage":{"type":"string","enum":["af","ar","az","bg","bn","ca","cs","cy","da","de","el","en","eo","es","et","eu","fa","fi","fo","fr","ga","gl","he","hi","hu","hy","id","is","it","ja","ka","kk","ko","ku","ky","lt","lv","mi","mn","mr","ms","mt","nb","nl","no","ns","pl","ps","pt","pt-br","qu","ro","ru","sk","sq","sv","sw","ta","te","th","tl","tn","tr","tt","uk","ur","uz","zh"],"title":"SupportedLanguage","description":"ISO code for a supported language."},"TextParam":{"properties":{"exposed":{"type":"boolean","title":"Exposed"},"default":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default"}},"type":"object","required":["exposed"],"title":"TextParam","description":"A text search parameter with exposure control."},"TextPart":{"properties":{"type":{"type":"string","const":"text","title":"Type","default":"text"},"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"TextPart"},"TextPartV4":{"properties":{"type":{"type":"string","const":"text","title":"Type","default":"text"},"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"TextPartV4"},"TextPartV5":{"properties":{"type":{"type":"string","const":"text","title":"Type","default":"text"},"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"TextPartV5"},"ToolApprovalRequestPart-Input":{"properties":{"type":{"type":"string","const":"tool-approval-request","title":"Type","default":"tool-approval-request"},"toolCallId":{"type":"string","title":"Toolcallid"},"toolName":{"type":"string","title":"Toolname"},"args":{"title":"Args"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"providerOptions":{"anyOf":[{},{"type":"null"}],"title":"Provideroptions"},"argsHash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Argshash"},"appId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Appid"}},"type":"object","required":["toolCallId","toolName","args"],"title":"ToolApprovalRequestPart"},"ToolApprovalRequestPart-Output":{"properties":{"type":{"type":"string","const":"tool-approval-request","title":"Type","default":"tool-approval-request"},"toolCallId":{"type":"string","title":"Toolcallid"},"toolName":{"type":"string","title":"Toolname"},"args":{"title":"Args"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"providerOptions":{"anyOf":[{},{"type":"null"}],"title":"Provideroptions"},"argsHash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Argshash"}},"type":"object","required":["toolCallId","toolName","args"],"title":"ToolApprovalRequestPart"},"ToolCallPart":{"properties":{"type":{"type":"string","const":"tool-call","title":"Type","default":"tool-call"},"toolCallId":{"type":"string","title":"Toolcallid"},"toolName":{"type":"string","title":"Toolname"},"args":{"title":"Args"},"requiresApproval":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Requiresapproval"},"providerOptions":{"anyOf":[{},{"type":"null"}],"title":"Provideroptions"}},"type":"object","required":["toolCallId","toolName","args"],"title":"ToolCallPart"},"ToolInvocationPartV4":{"properties":{"type":{"type":"string","const":"tool-invocation","title":"Type","default":"tool-invocation"},"toolInvocation":{"$ref":"#/components/schemas/ToolInvocationV4"}},"type":"object","required":["toolInvocation"],"title":"ToolInvocationPartV4"},"ToolInvocationV4":{"properties":{"toolCallId":{"type":"string","title":"Toolcallid"},"toolName":{"type":"string","title":"Toolname"},"args":{"additionalProperties":true,"type":"object","title":"Args"},"result":{"anyOf":[{},{"type":"null"}],"title":"Result"},"step":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Step"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"providerOptions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Provideroptions"},"requiresApproval":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Requiresapproval"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"argsHash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Argshash"}},"type":"object","required":["toolCallId","toolName"],"title":"ToolInvocationV4","description":"Model for tool invocation in a Message."},"ToolPartV5":{"properties":{"type":{"type":"string","title":"Type"},"toolCallId":{"type":"string","title":"Toolcallid"},"state":{"type":"string","enum":["input-streaming","input-available","output-available","output-error"],"title":"State","default":"output-available"},"input":{"additionalProperties":true,"type":"object","title":"Input"},"output":{"anyOf":[{},{"type":"null"}],"title":"Output"},"errorText":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Errortext"},"providerOptions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Provideroptions"},"requiresApproval":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Requiresapproval"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"argsHash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Argshash"}},"type":"object","required":["type","toolCallId"],"title":"ToolPartV5","description":"Model for tool invocation in a Message."},"ToolResultOutput":{"properties":{"type":{"$ref":"#/components/schemas/ToolResultOutputType"},"value":{"title":"Value"}},"type":"object","required":["type","value"],"title":"ToolResultOutput"},"ToolResultOutputType":{"type":"string","enum":["text","json","error-text","error-json","content"],"title":"ToolResultOutputType","description":"The valid 'type' of tool results."},"ToolResultPart-Input":{"properties":{"type":{"type":"string","const":"tool-result","title":"Type","default":"tool-result"},"toolCallId":{"type":"string","title":"Toolcallid"},"toolName":{"type":"string","title":"Toolname"},"output":{"$ref":"#/components/schemas/ToolResultOutput"},"providerOptions":{"anyOf":[{},{"type":"null"}],"title":"Provideroptions"}},"type":"object","required":["toolCallId","toolName","output"],"title":"ToolResultPart"},"ToolResultPart-Output":{"properties":{"type":{"type":"string","const":"tool-result","title":"Type","default":"tool-result"},"toolCallId":{"type":"string","title":"Toolcallid"},"toolName":{"type":"string","title":"Toolname"},"output":{"$ref":"#/components/schemas/ToolResultOutput"},"providerOptions":{"anyOf":[{},{"type":"null"}],"title":"Provideroptions"}},"type":"object","required":["toolCallId","toolName","output"],"title":"ToolResultPart"},"TypoToleranceEnum":{"type":"string","enum":["min","strict","true","false"],"title":"TypoToleranceEnum","description":"- `min`. Return matches with the lowest number of typos. For example, if you have matches without typos, only include those. But if there are no matches without typos (with 1 typo), include matches with 1 typo (2 typos). - `strict`. Return matches with the two lowest numbers of typos. With `strict`, the Typo ranking criterion is applied first in the `ranking` setting."},"UnknownToolConfig":{"properties":{"name":{"type":"string","maxLength":32,"minLength":3,"title":"Name"},"type":{"type":"string","const":"unknown","title":"Type","default":"unknown"}},"additionalProperties":true,"type":"object","required":["name"],"title":"UnknownToolConfig","description":"Exists only to ensure that when you change branch from toolX to feat/toolY, your config stays valid."},"UserDataResponse":{"properties":{"conversations":{"items":{"$ref":"#/components/schemas/ConversationFullResponse"},"type":"array","title":"Conversations"},"memories":{"items":{"$ref":"#/components/schemas/MemoryRecord"},"type":"array","title":"Memories"}},"type":"object","required":["conversations","memories"],"title":"UserDataResponse"},"UserMessageV4":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"role":{"type":"string","const":"user","title":"Role","default":"user"},"content":{"type":"string","title":"Content"},"parts":{"items":{"$ref":"#/components/schemas/TextPartV4"},"type":"array","title":"Parts"}},"type":"object","required":["content"],"title":"UserMessageV4"},"UserMessageV5":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"role":{"type":"string","const":"user","title":"Role","default":"user"},"parts":{"items":{"$ref":"#/components/schemas/TextPartV5"},"type":"array","title":"Parts"}},"type":"object","title":"UserMessageV5"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"Agents","description":"Manage your agents."},{"name":"Completions","description":"Generate completions"},{"name":"Providers","description":"Manage your LLM providers."},{"name":"Configurations","description":"Manage Agent Studio related application configurations."},{"name":"Secret Keys","description":"Manage secret keys used to authenticate requests."},{"name":"Internal","description":"Endpoints that are needed for internal or integration logic."}]} \ No newline at end of file diff --git a/go.mod b/go.mod index 1a67f678..9a2ae8fe 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24 github.com/cli/safeexec v1.0.1 github.com/dustin/go-humanize v1.0.1 - github.com/getkin/kin-openapi v0.100.0 + github.com/getkin/kin-openapi v0.135.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/go-version v1.7.0 github.com/mattn/go-colorable v0.1.14 @@ -49,7 +49,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/yaml v0.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -59,10 +58,14 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/oasdiff/yaml v0.0.9 // indirect + github.com/oasdiff/yaml3 v0.0.9 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/segmentio/backo-go v1.1.0 // indirect @@ -70,6 +73,7 @@ require ( github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/woodsbury/decimal128 v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/sys v0.31.0 // indirect diff --git a/go.sum b/go.sum index c747ca61..08e3b16c 100644 --- a/go.sum +++ b/go.sum @@ -38,14 +38,14 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/getkin/kin-openapi v0.100.0 h1:8L9xNFNJFDqIRjZwwFjWhTTmTAxPRn/BVTzPn+hOA2s= -github.com/getkin/kin-openapi v0.100.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/getkin/kin-openapi v0.135.0 h1:751SjYfbiwqukYuVjwYEIKNfrSwS5YpA7DZnKSwQgtg= +github.com/getkin/kin-openapi v0.135.0/go.mod h1:6dd5FJl6RdX4usBtFBaQhk9q62Yb2J0Mk5IhUO/QqFI= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -54,7 +54,6 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -63,25 +62,18 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -100,12 +92,20 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/oasdiff/yaml v0.0.9 h1:zQOvd2UKoozsSsAknnWoDJlSK4lC0mpmjfDsfqNwX48= +github.com/oasdiff/yaml v0.0.9/go.mod h1:8lvhgJG4xiKPj3HN5lDow4jZHPlx1i7dIwzkdAo6oAM= +github.com/oasdiff/yaml3 v0.0.9 h1:rWPrKccrdUm8J0F3sGuU+fuh9+1K/RdJlWF7O/9yw2g= +github.com/oasdiff/yaml3 v0.0.9/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -114,8 +114,9 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= @@ -142,14 +143,16 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= +github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -194,15 +197,11 @@ golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= diff --git a/pkg/cmd/agentstudio/agents/agents.go b/pkg/cmd/agentstudio/agents/agents.go new file mode 100644 index 00000000..72b8c7a0 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/agents.go @@ -0,0 +1,41 @@ +package agents + +import ( + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/complete" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/create" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/delete" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/duplicate" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/get" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/list" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/publish" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/unpublish" + "github.com/algolia/cli/pkg/cmd/agentstudio/agents/update" + "github.com/algolia/cli/pkg/cmdutil" +) + +// NewAgentsCmd returns the parent command for agent operations. +func NewAgentsCmd(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "agents", + Aliases: []string{"agent"}, + Short: "Manage Agent Studio agents", + Long: heredoc.Doc(` + Create, list, update, run, and manage Agent Studio agents. + `), + } + + cmd.AddCommand(list.NewListCmd(f, nil)) + cmd.AddCommand(get.NewGetCmd(f, nil)) + cmd.AddCommand(create.NewCreateCmd(f, nil)) + cmd.AddCommand(update.NewUpdateCmd(f, nil)) + cmd.AddCommand(delete.NewDeleteCmd(f, nil)) + cmd.AddCommand(duplicate.NewDuplicateCmd(f, nil)) + cmd.AddCommand(publish.NewPublishCmd(f, nil)) + cmd.AddCommand(unpublish.NewUnpublishCmd(f, nil)) + cmd.AddCommand(complete.NewCompleteCmd(f, nil)) + + return cmd +} diff --git a/pkg/cmd/agentstudio/agents/complete/complete.go b/pkg/cmd/agentstudio/agents/complete/complete.go new file mode 100644 index 00000000..da3121dc --- /dev/null +++ b/pkg/cmd/agentstudio/agents/complete/complete.go @@ -0,0 +1,102 @@ +package complete + +import ( + "io" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +var validCompatibilityModes = map[string]bool{ + "ai-sdk-4": true, + "ai-sdk-5": true, +} + +type CompleteOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + AgentID string + File string + Body agentstudio.AgentCompletionRequest + CompatibilityMode string + Stream bool + Cache bool +} + +func NewCompleteCmd(f *cmdutil.Factory, runF func(*CompleteOptions) error) *cobra.Command { + opts := &CompleteOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + } + cmd := &cobra.Command{ + Use: "complete ", + Aliases: []string{"completion", "run"}, + Args: cobra.ExactArgs(1), + Short: "Run a completion against an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # Run with a JSON file holding messages + $ algolia agentstudio agents complete a1b2 -F request.json + + # Continue an existing conversation + $ algolia agentstudio agents complete a1b2 --id conv-1 --messages '[{"role":"user","parts":[{"type":"text","text":"hi"}]}]' + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.AgentID = args[0] + if !validCompatibilityModes[opts.CompatibilityMode] { + return cmdutil.FlagErrorf( + "invalid --compatibility-mode %q: must be one of ai-sdk-4, ai-sdk-5", + opts.CompatibilityMode, + ) + } + if err := cmdutil.MergeFileAndFlagsInto(opts.IO, opts.File, cmd, cmdutil.AgentCompletionRequest, &opts.Body); err != nil { + return err + } + if runF != nil { + return runF(opts) + } + return runCompleteCmd(opts) + }, + } + + cmd.Flags().StringVarP(&opts.File, "file", "F", "", "Completion request JSON `file` (use \"-\" for stdin)") + cmd.Flags().StringVar(&opts.CompatibilityMode, "compatibility-mode", "ai-sdk-5", "API compatibility mode. One of: ai-sdk-4, ai-sdk-5") + cmd.Flags().BoolVar(&opts.Stream, "stream", false, "Stream the response as SSE bytes instead of waiting for the full JSON body") + cmd.Flags().BoolVar(&opts.Cache, "cache", true, "Allow the API to return cached responses") + cmdutil.AddAgentCompletionRequestFlags(cmd) + return cmd +} + +func runCompleteCmd(opts *CompleteOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Running completion") + body, err := client.CreateCompletion(opts.AgentID, opts.Body, agentstudio.CompletionParams{ + CompatibilityMode: opts.CompatibilityMode, + Stream: &opts.Stream, + Cache: &opts.Cache, + }) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + defer body.Close() + + if _, err := io.Copy(opts.IO.Out, body); err != nil { + return err + } + return nil +} diff --git a/pkg/cmd/agentstudio/agents/complete/complete_test.go b/pkg/cmd/agentstudio/agents/complete/complete_test.go new file mode 100644 index 00000000..4ac908f1 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/complete/complete_test.go @@ -0,0 +1,39 @@ +package complete + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewCompleteCmd_rejectsBadCompatibilityMode(t *testing.T) { + r := httpmock.Registry{} + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewCompleteCmd(f, nil) + _, err := test.Execute(cmd, `a1 --compatibility-mode bogus`, out) + require.Error(t, err) + assert.Contains(t, err.Error(), "ai-sdk-4, ai-sdk-5") +} + +func Test_NewCompleteCmd_postsBodyAndStreamsResponse(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodPost, "1/agents/a1/completions"), + httpmock.JSONResponse(map[string]any{"answer": "hi"}), + ) + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewCompleteCmd(f, nil) + _, err := test.Execute(cmd, `a1 --id conv-1`, out) + require.NoError(t, err) + assert.Contains(t, out.String(), `"answer"`) + assert.Contains(t, out.String(), `"hi"`) +} diff --git a/pkg/cmd/agentstudio/agents/create/create.go b/pkg/cmd/agentstudio/agents/create/create.go new file mode 100644 index 00000000..bc84a091 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/create/create.go @@ -0,0 +1,93 @@ +package create + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type CreateOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + File string + Body agentstudio.AgentConfigCreate + + PrintFlags *cmdutil.PrintFlags +} + +func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command { + opts := &CreateOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "create", + Args: cobra.NoArgs, + Short: "Create an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # Create from a JSON file + $ algolia agentstudio agents create -F agent.json + + # Create from individual flags + $ algolia agentstudio agents create --name "Helper" --instructions "Be helpful" + `), + RunE: func(cmd *cobra.Command, args []string) error { + if err := cmdutil.MergeFileAndFlagsInto(opts.IO, opts.File, cmd, cmdutil.AgentConfigCreate, &opts.Body); err != nil { + return err + } + if opts.Body.Name == "" || opts.Body.Instructions == "" { + return fmt.Errorf("--name and --instructions are required (or provide them in -F file)") + } + if runF != nil { + return runF(opts) + } + return runCreateCmd(opts) + }, + } + + cmd.Flags().StringVarP(&opts.File, "file", "F", "", "Agent config JSON `file` (use \"-\" for stdin)") + cmdutil.AddAgentConfigCreateFlags(cmd) + opts.PrintFlags.AddFlags(cmd) + + return cmd +} + +func runCreateCmd(opts *CreateOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + + opts.IO.StartProgressIndicatorWithLabel("Creating agent") + agent, err := client.CreateAgent(opts.Body) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Created agent %s (%s)\n", cs.SuccessIcon(), agent.Name, agent.ID) + return nil + } + + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, agent) +} diff --git a/pkg/cmd/agentstudio/agents/create/create_test.go b/pkg/cmd/agentstudio/agents/create/create_test.go new file mode 100644 index 00000000..a9aef4ea --- /dev/null +++ b/pkg/cmd/agentstudio/agents/create/create_test.go @@ -0,0 +1,46 @@ +package create + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewCreateCmd_requiresNameAndInstructions(t *testing.T) { + r := httpmock.Registry{} + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewCreateCmd(f, nil) + _, err := test.Execute(cmd, "", out) + require.Error(t, err) + assert.Contains(t, err.Error(), "--name") +} + +func Test_NewCreateCmd_postsBodyAndPrintsSuccess(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodPost, "1/agents"), + httpmock.JSONResponse(map[string]any{ + "id": "a1", + "name": "Helper", + "status": "draft", + "instructions": "be helpful", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewCreateCmd(f, nil) + _, err := test.Execute(cmd, `--name Helper --instructions "be helpful"`, out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Created agent Helper") + assert.Contains(t, out.String(), "a1") +} diff --git a/pkg/cmd/agentstudio/agents/delete/delete.go b/pkg/cmd/agentstudio/agents/delete/delete.go new file mode 100644 index 00000000..d45de5d9 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/delete/delete.go @@ -0,0 +1,76 @@ +package delete + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type DeleteOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + ID string + Confirm bool +} + +func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command { + opts := &DeleteOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + } + cmd := &cobra.Command{ + Use: "delete ", + Aliases: []string{"rm"}, + Args: cobra.ExactArgs(1), + Short: "Delete an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + $ algolia agentstudio agents delete a1b2 --confirm + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ID = args[0] + if !opts.Confirm { + return fmt.Errorf("--confirm is required to delete agent %s", opts.ID) + } + if runF != nil { + return runF(opts) + } + return runDeleteCmd(opts) + }, + } + + cmd.Flags().BoolVar(&opts.Confirm, "confirm", false, "Skip the confirmation prompt and delete immediately") + return cmd +} + +func runDeleteCmd(opts *DeleteOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + + opts.IO.StartProgressIndicatorWithLabel("Deleting agent") + err = client.DeleteAgent(opts.ID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Deleted agent %s\n", cs.SuccessIcon(), opts.ID) + } + return nil +} diff --git a/pkg/cmd/agentstudio/agents/delete/delete_test.go b/pkg/cmd/agentstudio/agents/delete/delete_test.go new file mode 100644 index 00000000..0152cbb8 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/delete/delete_test.go @@ -0,0 +1,38 @@ +package delete + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewDeleteCmd_requiresConfirm(t *testing.T) { + r := httpmock.Registry{} + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewDeleteCmd(f, nil) + _, err := test.Execute(cmd, "a1", out) + require.Error(t, err) + assert.Contains(t, err.Error(), "--confirm") +} + +func Test_NewDeleteCmd_callsAPI(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodDelete, "1/agents/a1"), + httpmock.JSONResponse(""), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewDeleteCmd(f, nil) + _, err := test.Execute(cmd, "a1 --confirm", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Deleted agent a1") +} diff --git a/pkg/cmd/agentstudio/agents/duplicate/duplicate.go b/pkg/cmd/agentstudio/agents/duplicate/duplicate.go new file mode 100644 index 00000000..1aa358fc --- /dev/null +++ b/pkg/cmd/agentstudio/agents/duplicate/duplicate.go @@ -0,0 +1,75 @@ +package duplicate + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type DuplicateOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + ID string + PrintFlags *cmdutil.PrintFlags +} + +func NewDuplicateCmd(f *cmdutil.Factory, runF func(*DuplicateOptions) error) *cobra.Command { + opts := &DuplicateOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "duplicate ", + Args: cobra.ExactArgs(1), + Short: "Duplicate an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + $ algolia agentstudio agents duplicate a1b2 + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ID = args[0] + if runF != nil { + return runF(opts) + } + return runDuplicateCmd(opts) + }, + } + opts.PrintFlags.AddFlags(cmd) + return cmd +} + +func runDuplicateCmd(opts *DuplicateOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Duplicating agent") + agent, err := client.DuplicateAgent(opts.ID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Duplicated %s -> %s\n", cs.SuccessIcon(), opts.ID, agent.ID) + return nil + } + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, agent) +} diff --git a/pkg/cmd/agentstudio/agents/duplicate/duplicate_test.go b/pkg/cmd/agentstudio/agents/duplicate/duplicate_test.go new file mode 100644 index 00000000..85630c78 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/duplicate/duplicate_test.go @@ -0,0 +1,34 @@ +package duplicate + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewDuplicateCmd_postsAndPrintsResult(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodPost, "1/agents/a1/duplicate"), + httpmock.JSONResponse(map[string]any{ + "id": "a1-copy", + "name": "Helper (copy)", + "status": "draft", + "instructions": "be helpful", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewDuplicateCmd(f, nil) + _, err := test.Execute(cmd, "a1", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Duplicated a1 -> a1-copy") +} diff --git a/pkg/cmd/agentstudio/agents/get/get.go b/pkg/cmd/agentstudio/agents/get/get.go new file mode 100644 index 00000000..f3dea455 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/get/get.go @@ -0,0 +1,73 @@ +package get + +import ( + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type GetOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + ID string + + PrintFlags *cmdutil.PrintFlags +} + +func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command { + opts := &GetOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "get ", + Args: cobra.ExactArgs(1), + Short: "Get an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # Get an agent by ID + $ algolia agentstudio agents get a1b2c3 + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ID = args[0] + if runF != nil { + return runF(opts) + } + return runGetCmd(opts) + }, + } + + opts.PrintFlags.AddFlags(cmd) + return cmd +} + +func runGetCmd(opts *GetOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + + opts.IO.StartProgressIndicatorWithLabel("Fetching agent") + agent, err := client.GetAgent(opts.ID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, agent) +} diff --git a/pkg/cmd/agentstudio/agents/get/get_test.go b/pkg/cmd/agentstudio/agents/get/get_test.go new file mode 100644 index 00000000..8b0d1272 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/get/get_test.go @@ -0,0 +1,29 @@ +package get + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/algolia/cli/test" +) + +func Test_NewGetCmd_flagBinding(t *testing.T) { + f, out := test.NewFactory(false, nil, nil, "") + + called := false + cmd := NewGetCmd(f, func(opts *GetOptions) error { + called = true + assert.Equal(t, "a1", opts.ID) + if assert.NotNil(t, opts.PrintFlags.OutputFormat) { + assert.Equal(t, "json", *opts.PrintFlags.OutputFormat) + } + return nil + }) + + _, err := test.Execute(cmd, "a1", out) + if err != nil { + t.Fatal(err) + } + assert.True(t, called) +} diff --git a/pkg/cmd/agentstudio/agents/list/list.go b/pkg/cmd/agentstudio/agents/list/list.go new file mode 100644 index 00000000..115f9e4b --- /dev/null +++ b/pkg/cmd/agentstudio/agents/list/list.go @@ -0,0 +1,101 @@ +package list + +import ( + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" + "github.com/algolia/cli/pkg/printers" + "github.com/algolia/cli/pkg/validators" +) + +type ListOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + Page int + Limit int + + PrintFlags *cmdutil.PrintFlags +} + +func NewListCmd(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command { + opts := &ListOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags(), + } + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"l"}, + Args: validators.NoArgs(), + Short: "List Agent Studio agents", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # List the first page of agents + $ algolia agentstudio agents list + + # Page through results + $ algolia agentstudio agents list --page 2 --limit 50 + `), + RunE: func(cmd *cobra.Command, args []string) error { + if runF != nil { + return runF(opts) + } + return runListCmd(opts) + }, + } + + cmd.Flags().IntVar(&opts.Page, "page", 0, "Page number (1-based; default API behavior when omitted)") + cmd.Flags().IntVar(&opts.Limit, "limit", 0, "Items per page (default API behavior when omitted)") + opts.PrintFlags.AddFlags(cmd) + + return cmd +} + +func runListCmd(opts *ListOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + + opts.IO.StartProgressIndicatorWithLabel("Fetching agents") + res, err := client.ListAgents(opts.Page, opts.Limit) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + + if opts.PrintFlags.OutputFlagSpecified() && opts.PrintFlags.OutputFormat != nil { + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, res) + } + + table := printers.NewTablePrinter(opts.IO) + if table.IsTTY() { + table.AddField("ID", nil, nil) + table.AddField("NAME", nil, nil) + table.AddField("STATUS", nil, nil) + table.AddField("UPDATED", nil, nil) + table.EndRow() + } + for _, a := range res.Data { + table.AddField(a.ID, nil, nil) + table.AddField(a.Name, nil, nil) + table.AddField(a.Status, nil, nil) + table.AddField(a.UpdatedAt, nil, nil) + table.EndRow() + } + return table.Render() +} diff --git a/pkg/cmd/agentstudio/agents/list/list_test.go b/pkg/cmd/agentstudio/agents/list/list_test.go new file mode 100644 index 00000000..4bff0048 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/list/list_test.go @@ -0,0 +1,56 @@ +package list + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewListCmd_rendersTable(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodGet, "1/agents"), + httpmock.JSONResponse(map[string]any{ + "data": []any{map[string]any{ + "id": "a1", + "name": "Helper", + "status": "draft", + "instructions": "be helpful", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }}, + "pagination": map[string]any{"page": 1, "limit": 10, "totalCount": 1, "totalPages": 1}, + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewListCmd(f, nil) + _, err := test.Execute(cmd, "", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "a1") + assert.Contains(t, out.String(), "Helper") +} + +func Test_NewListCmd_outputJSONPasses(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodGet, "1/agents"), + httpmock.JSONResponse(map[string]any{ + "data": []any{}, + "pagination": map[string]any{"page": 1, "limit": 10, "totalCount": 0, "totalPages": 0}, + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewListCmd(f, nil) + _, err := test.Execute(cmd, "--output json", out) + require.NoError(t, err) + assert.Contains(t, out.String(), `"pagination"`) +} diff --git a/pkg/cmd/agentstudio/agents/publish/publish.go b/pkg/cmd/agentstudio/agents/publish/publish.go new file mode 100644 index 00000000..360f66b6 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/publish/publish.go @@ -0,0 +1,75 @@ +package publish + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type PublishOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + ID string + PrintFlags *cmdutil.PrintFlags +} + +func NewPublishCmd(f *cmdutil.Factory, runF func(*PublishOptions) error) *cobra.Command { + opts := &PublishOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "publish ", + Args: cobra.ExactArgs(1), + Short: "Publish an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + $ algolia agentstudio agents publish a1b2 + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ID = args[0] + if runF != nil { + return runF(opts) + } + return runPublishCmd(opts) + }, + } + opts.PrintFlags.AddFlags(cmd) + return cmd +} + +func runPublishCmd(opts *PublishOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Publishing agent") + agent, err := client.PublishAgent(opts.ID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Published agent %s\n", cs.SuccessIcon(), agent.ID) + return nil + } + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, agent) +} diff --git a/pkg/cmd/agentstudio/agents/publish/publish_test.go b/pkg/cmd/agentstudio/agents/publish/publish_test.go new file mode 100644 index 00000000..5e84a880 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/publish/publish_test.go @@ -0,0 +1,34 @@ +package publish + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewPublishCmd_callsEndpoint(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodPost, "1/agents/a1/publish"), + httpmock.JSONResponse(map[string]any{ + "id": "a1", + "name": "Helper", + "status": "published", + "instructions": "be helpful", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewPublishCmd(f, nil) + _, err := test.Execute(cmd, "a1", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Published agent a1") +} diff --git a/pkg/cmd/agentstudio/agents/unpublish/unpublish.go b/pkg/cmd/agentstudio/agents/unpublish/unpublish.go new file mode 100644 index 00000000..94a9915a --- /dev/null +++ b/pkg/cmd/agentstudio/agents/unpublish/unpublish.go @@ -0,0 +1,75 @@ +package unpublish + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type UnpublishOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + ID string + PrintFlags *cmdutil.PrintFlags +} + +func NewUnpublishCmd(f *cmdutil.Factory, runF func(*UnpublishOptions) error) *cobra.Command { + opts := &UnpublishOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "unpublish ", + Args: cobra.ExactArgs(1), + Short: "Unpublish an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + $ algolia agentstudio agents unpublish a1b2 + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ID = args[0] + if runF != nil { + return runF(opts) + } + return runUnpublishCmd(opts) + }, + } + opts.PrintFlags.AddFlags(cmd) + return cmd +} + +func runUnpublishCmd(opts *UnpublishOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Unpublishing agent") + agent, err := client.UnpublishAgent(opts.ID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Unpublished agent %s\n", cs.SuccessIcon(), agent.ID) + return nil + } + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, agent) +} diff --git a/pkg/cmd/agentstudio/agents/unpublish/unpublish_test.go b/pkg/cmd/agentstudio/agents/unpublish/unpublish_test.go new file mode 100644 index 00000000..346f4327 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/unpublish/unpublish_test.go @@ -0,0 +1,34 @@ +package unpublish + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewUnpublishCmd_callsEndpoint(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodPost, "1/agents/a1/unpublish"), + httpmock.JSONResponse(map[string]any{ + "id": "a1", + "name": "Helper", + "status": "draft", + "instructions": "be helpful", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewUnpublishCmd(f, nil) + _, err := test.Execute(cmd, "a1", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Unpublished agent a1") +} diff --git a/pkg/cmd/agentstudio/agents/update/update.go b/pkg/cmd/agentstudio/agents/update/update.go new file mode 100644 index 00000000..78aa39f0 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/update/update.go @@ -0,0 +1,94 @@ +package update + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type UpdateOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + ID string + File string + Body agentstudio.AgentConfigUpdate + + PrintFlags *cmdutil.PrintFlags +} + +func NewUpdateCmd(f *cmdutil.Factory, runF func(*UpdateOptions) error) *cobra.Command { + opts := &UpdateOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "update ", + Args: cobra.ExactArgs(1), + Short: "Update an Agent Studio agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # Update from a JSON file + $ algolia agentstudio agents update a1b2 -F changes.json + + # Update one field via flag + $ algolia agentstudio agents update a1b2 --instructions "be more helpful" + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ID = args[0] + // Update reuses the create-flag set (same property names; all + // optional in the update body). + if err := cmdutil.MergeFileAndFlagsInto(opts.IO, opts.File, cmd, cmdutil.AgentConfigCreate, &opts.Body); err != nil { + return err + } + if runF != nil { + return runF(opts) + } + return runUpdateCmd(opts) + }, + } + + cmd.Flags().StringVarP(&opts.File, "file", "F", "", "Agent config JSON `file` (use \"-\" for stdin)") + cmdutil.AddAgentConfigCreateFlags(cmd) + opts.PrintFlags.AddFlags(cmd) + + return cmd +} + +func runUpdateCmd(opts *UpdateOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + + opts.IO.StartProgressIndicatorWithLabel("Updating agent") + agent, err := client.UpdateAgent(opts.ID, opts.Body) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Updated agent %s\n", cs.SuccessIcon(), agent.ID) + return nil + } + + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, agent) +} diff --git a/pkg/cmd/agentstudio/agents/update/update_test.go b/pkg/cmd/agentstudio/agents/update/update_test.go new file mode 100644 index 00000000..a8028aa0 --- /dev/null +++ b/pkg/cmd/agentstudio/agents/update/update_test.go @@ -0,0 +1,35 @@ +package update + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewUpdateCmd_appliesFlagAsBody(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodPatch, "1/agents/a1"), + httpmock.JSONResponse(map[string]any{ + "id": "a1", + "name": "Helper", + "description": "renamed via CLI", + "status": "draft", + "instructions": "be helpful", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewUpdateCmd(f, nil) + _, err := test.Execute(cmd, `a1 --description "renamed via CLI"`, out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Updated agent a1") +} diff --git a/pkg/cmd/agentstudio/agentstudio.go b/pkg/cmd/agentstudio/agentstudio.go new file mode 100644 index 00000000..f017e3c3 --- /dev/null +++ b/pkg/cmd/agentstudio/agentstudio.go @@ -0,0 +1,44 @@ +package agentstudio + +import ( + "errors" + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/pkg/cmd/agentstudio/agents" + "github.com/algolia/cli/pkg/cmd/agentstudio/conversations" + "github.com/algolia/cli/pkg/cmdutil" +) + +const authMethodHelpMsg = `In order to use the 'agentstudio' commands, you need an Algolia application ID and an admin API key. Set them via: + - The ALGOLIA_APPLICATION_ID and ALGOLIA_API_KEY environment variables + - Your profile (run 'algolia profile add' or edit ~/.config/algolia/config.tml)` + +// NewAgentStudioCmd returns the top-level Agent Studio command. +func NewAgentStudioCmd(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "agentstudio", + Aliases: []string{"agent-studio", "as"}, + Short: "Manage Algolia Agent Studio agents and conversations", + Long: heredoc.Docf(` + Manage Algolia Agent Studio (RAG API) agents, conversations, and completions. + + %s + `, authMethodHelpMsg), + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if _, err := f.AgentStudioClient(); err != nil { + fmt.Fprintf(f.IOStreams.ErrOut, "Agent Studio authentication error: %s\n\n", err) + fmt.Fprintln(f.IOStreams.ErrOut, authMethodHelpMsg) + return errors.New("authError") + } + return nil + }, + } + + cmd.AddCommand(agents.NewAgentsCmd(f)) + cmd.AddCommand(conversations.NewConversationsCmd(f)) + + return cmd +} diff --git a/pkg/cmd/agentstudio/conversations/conversations.go b/pkg/cmd/agentstudio/conversations/conversations.go new file mode 100644 index 00000000..3a74350a --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/conversations.go @@ -0,0 +1,30 @@ +package conversations + +import ( + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/pkg/cmd/agentstudio/conversations/delete" + "github.com/algolia/cli/pkg/cmd/agentstudio/conversations/export" + "github.com/algolia/cli/pkg/cmd/agentstudio/conversations/get" + "github.com/algolia/cli/pkg/cmd/agentstudio/conversations/list" + "github.com/algolia/cli/pkg/cmdutil" +) + +func NewConversationsCmd(f *cmdutil.Factory) *cobra.Command { + cmd := &cobra.Command{ + Use: "conversations", + Aliases: []string{"conv", "conversation"}, + Short: "Inspect Agent Studio conversations", + Long: heredoc.Doc(` + List, fetch, delete, and export conversations recorded for an agent. + `), + } + + cmd.AddCommand(list.NewListCmd(f, nil)) + cmd.AddCommand(get.NewGetCmd(f, nil)) + cmd.AddCommand(delete.NewDeleteCmd(f, nil)) + cmd.AddCommand(export.NewExportCmd(f, nil)) + + return cmd +} diff --git a/pkg/cmd/agentstudio/conversations/delete/delete.go b/pkg/cmd/agentstudio/conversations/delete/delete.go new file mode 100644 index 00000000..8d511096 --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/delete/delete.go @@ -0,0 +1,98 @@ +package delete + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type DeleteOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + AgentID string + ConversationID string + All bool + Confirm bool +} + +func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command { + opts := &DeleteOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + } + cmd := &cobra.Command{ + Use: "delete [conversation_id]", + Aliases: []string{"rm"}, + Args: cobra.RangeArgs(1, 2), + Short: "Delete a conversation, or all conversations for an agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # Delete a single conversation + $ algolia agentstudio conversations delete a1b2 conv-1 --confirm + + # Delete every conversation for the agent + $ algolia agentstudio conversations delete a1b2 --all --confirm + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.AgentID = args[0] + if len(args) == 2 { + opts.ConversationID = args[1] + } + if !opts.All && opts.ConversationID == "" { + return fmt.Errorf("either pass or use --all") + } + if opts.All && opts.ConversationID != "" { + return fmt.Errorf("--all conflicts with a positional ") + } + if !opts.Confirm { + return fmt.Errorf("--confirm is required to delete") + } + if runF != nil { + return runF(opts) + } + return runDeleteCmd(opts) + }, + } + + cmd.Flags().BoolVar(&opts.All, "all", false, "Delete every conversation for the agent") + cmd.Flags().BoolVar(&opts.Confirm, "confirm", false, "Skip the confirmation prompt and delete immediately") + return cmd +} + +func runDeleteCmd(opts *DeleteOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Deleting conversation(s)") + if opts.All { + err = client.DeleteAllConversations(opts.AgentID) + } else { + err = client.DeleteConversation(opts.AgentID, opts.ConversationID) + } + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + if opts.All { + fmt.Fprintf(opts.IO.Out, "%s Deleted all conversations for agent %s\n", cs.SuccessIcon(), opts.AgentID) + } else { + fmt.Fprintf(opts.IO.Out, "%s Deleted conversation %s\n", cs.SuccessIcon(), opts.ConversationID) + } + } + return nil +} diff --git a/pkg/cmd/agentstudio/conversations/delete/delete_test.go b/pkg/cmd/agentstudio/conversations/delete/delete_test.go new file mode 100644 index 00000000..9017b062 --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/delete/delete_test.go @@ -0,0 +1,64 @@ +package delete + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewDeleteCmd_requiresConfirm(t *testing.T) { + r := httpmock.Registry{} + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewDeleteCmd(f, nil) + _, err := test.Execute(cmd, "a1 conv-1", out) + require.Error(t, err) + assert.Contains(t, err.Error(), "--confirm") +} + +func Test_NewDeleteCmd_rejectsAllPlusConvID(t *testing.T) { + r := httpmock.Registry{} + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewDeleteCmd(f, nil) + _, err := test.Execute(cmd, "a1 conv-1 --all --confirm", out) + require.Error(t, err) + assert.Contains(t, err.Error(), "--all conflicts") +} + +func Test_NewDeleteCmd_singleConversation(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodDelete, "1/agents/a1/conversations/conv-1"), + httpmock.JSONResponse(""), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewDeleteCmd(f, nil) + _, err := test.Execute(cmd, "a1 conv-1 --confirm", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Deleted conversation conv-1") +} + +func Test_NewDeleteCmd_allConversations(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodDelete, "1/agents/a1/conversations"), + httpmock.JSONResponse(""), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewDeleteCmd(f, nil) + _, err := test.Execute(cmd, "a1 --all --confirm", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "Deleted all conversations for agent a1") +} diff --git a/pkg/cmd/agentstudio/conversations/export/export.go b/pkg/cmd/agentstudio/conversations/export/export.go new file mode 100644 index 00000000..09902f62 --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/export/export.go @@ -0,0 +1,95 @@ +package export + +import ( + "fmt" + "io" + "os" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type ExportOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + AgentID string + Output string +} + +func NewExportCmd(f *cmdutil.Factory, runF func(*ExportOptions) error) *cobra.Command { + opts := &ExportOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + } + cmd := &cobra.Command{ + Use: "export ", + Args: cobra.ExactArgs(1), + Short: "Export an agent's conversations as raw bytes", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + # Print to stdout + $ algolia agentstudio conversations export a1b2 + + # Save to a file + $ algolia agentstudio conversations export a1b2 -o conversations.json + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.AgentID = args[0] + if runF != nil { + return runF(opts) + } + return runExportCmd(opts) + }, + } + cmd.Flags().StringVarP(&opts.Output, "output-file", "o", "", "Write the export body to the named file instead of stdout") + return cmd +} + +func runExportCmd(opts *ExportOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Exporting conversations") + body, err := client.ExportConversations(opts.AgentID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + defer body.Close() + + if opts.Output != "" { + // 0600: conversation exports may contain user PII / prompts. + f, err := os.OpenFile(opts.Output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) + if err != nil { + return err + } + n, copyErr := io.Copy(f, body) + closeErr := f.Close() + if copyErr != nil { + return copyErr + } + if closeErr != nil { + return closeErr + } + if opts.IO.IsStdoutTTY() { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, "%s Wrote %d bytes to %s\n", cs.SuccessIcon(), n, opts.Output) + } + return nil + } + + _, err = io.Copy(opts.IO.Out, body) + return err +} diff --git a/pkg/cmd/agentstudio/conversations/export/export_test.go b/pkg/cmd/agentstudio/conversations/export/export_test.go new file mode 100644 index 00000000..93fb3c6e --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/export/export_test.go @@ -0,0 +1,50 @@ +package export + +import ( + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewExportCmd_writesToStdout(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodGet, "1/agents/a1/conversations/export"), + httpmock.StringResponse(`{"conversations":[]}`), + ) + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewExportCmd(f, nil) + _, err := test.Execute(cmd, "a1", out) + require.NoError(t, err) + assert.Equal(t, `{"conversations":[]}`, out.String()) +} + +func Test_NewExportCmd_writesToFile(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodGet, "1/agents/a1/conversations/export"), + httpmock.StringResponse(`payload`), + ) + defer r.Verify(t) + + dir := t.TempDir() + dest := filepath.Join(dir, "out.json") + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewExportCmd(f, nil) + _, err := test.Execute(cmd, "a1 -o "+dest, out) + require.NoError(t, err) + + got, err := os.ReadFile(dest) + require.NoError(t, err) + assert.Equal(t, "payload", string(got)) +} diff --git a/pkg/cmd/agentstudio/conversations/get/get.go b/pkg/cmd/agentstudio/conversations/get/get.go new file mode 100644 index 00000000..bc2b69a1 --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/get/get.go @@ -0,0 +1,71 @@ +package get + +import ( + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" +) + +type GetOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + AgentID string + ConversationID string + + PrintFlags *cmdutil.PrintFlags +} + +func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command { + opts := &GetOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), + } + cmd := &cobra.Command{ + Use: "get ", + Args: cobra.ExactArgs(2), + Short: "Get a conversation", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + $ algolia agentstudio conversations get a1b2 conv-1 + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.AgentID = args[0] + opts.ConversationID = args[1] + if runF != nil { + return runF(opts) + } + return runGetCmd(opts) + }, + } + opts.PrintFlags.AddFlags(cmd) + return cmd +} + +func runGetCmd(opts *GetOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Fetching conversation") + conv, err := client.GetConversation(opts.AgentID, opts.ConversationID) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, conv) +} diff --git a/pkg/cmd/agentstudio/conversations/get/get_test.go b/pkg/cmd/agentstudio/conversations/get/get_test.go new file mode 100644 index 00000000..f2818c6d --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/get/get_test.go @@ -0,0 +1,35 @@ +package get + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewGetCmd_callsEndpoint(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodGet, "1/agents/a1/conversations/conv-1"), + httpmock.JSONResponse(map[string]any{ + "id": "conv-1", + "agentId": "a1", + "title": "Hello", + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + "messages": []any{}, + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(false, &r, nil, "") + cmd := NewGetCmd(f, nil) + _, err := test.Execute(cmd, "a1 conv-1", out) + require.NoError(t, err) + assert.Contains(t, out.String(), `"conv-1"`) + assert.Contains(t, out.String(), `"Hello"`) +} diff --git a/pkg/cmd/agentstudio/conversations/list/list.go b/pkg/cmd/agentstudio/conversations/list/list.go new file mode 100644 index 00000000..9c9309e0 --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/list/list.go @@ -0,0 +1,104 @@ +package list + +import ( + "strconv" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + + "github.com/algolia/cli/api/agentstudio" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/iostreams" + "github.com/algolia/cli/pkg/printers" +) + +type ListOptions struct { + Config config.IConfig + IO *iostreams.IOStreams + + AgentStudioClient func() (*agentstudio.Client, error) + + AgentID string + Page int + Limit int + + PrintFlags *cmdutil.PrintFlags +} + +func NewListCmd(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command { + opts := &ListOptions{ + IO: f.IOStreams, + Config: f.Config, + AgentStudioClient: f.AgentStudioClient, + PrintFlags: cmdutil.NewPrintFlags(), + } + cmd := &cobra.Command{ + Use: "list ", + Args: cobra.ExactArgs(1), + Short: "List conversations for an agent", + Annotations: map[string]string{ + "acls": "admin", + }, + Example: heredoc.Doc(` + $ algolia agentstudio conversations list a1b2 --limit 50 + `), + RunE: func(cmd *cobra.Command, args []string) error { + opts.AgentID = args[0] + if runF != nil { + return runF(opts) + } + return runListCmd(opts) + }, + } + cmd.Flags().IntVar(&opts.Page, "page", 0, "Page number (1-based; default API behavior when omitted)") + cmd.Flags().IntVar(&opts.Limit, "limit", 0, "Items per page (default API behavior when omitted)") + opts.PrintFlags.AddFlags(cmd) + return cmd +} + +func runListCmd(opts *ListOptions) error { + client, err := opts.AgentStudioClient() + if err != nil { + return err + } + opts.IO.StartProgressIndicatorWithLabel("Fetching conversations") + res, err := client.ListConversations(opts.AgentID, opts.Page, opts.Limit) + opts.IO.StopProgressIndicator() + if err != nil { + return err + } + + if opts.PrintFlags.OutputFlagSpecified() && opts.PrintFlags.OutputFormat != nil { + p, err := opts.PrintFlags.ToPrinter() + if err != nil { + return err + } + return p.Print(opts.IO, res) + } + + table := printers.NewTablePrinter(opts.IO) + if table.IsTTY() { + table.AddField("ID", nil, nil) + table.AddField("TITLE", nil, nil) + table.AddField("MESSAGES", nil, nil) + table.AddField("UPDATED", nil, nil) + table.EndRow() + } + for _, c := range res.Data { + title := "" + if c.Title != nil { + title = *c.Title + } + count := "" + if c.MessageCount != nil { + count = strconv.Itoa(*c.MessageCount) + } + table.AddField(c.ID, nil, nil) + table.AddField(title, nil, nil) + table.AddField(count, nil, nil) + table.AddField(c.UpdatedAt, nil, nil) + table.EndRow() + } + return table.Render() +} diff --git a/pkg/cmd/agentstudio/conversations/list/list_test.go b/pkg/cmd/agentstudio/conversations/list/list_test.go new file mode 100644 index 00000000..e3e2b2ee --- /dev/null +++ b/pkg/cmd/agentstudio/conversations/list/list_test.go @@ -0,0 +1,39 @@ +package list + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func Test_NewListCmd_rendersTable(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST(http.MethodGet, "1/agents/a1/conversations"), + httpmock.JSONResponse(map[string]any{ + "data": []any{map[string]any{ + "id": "conv-1", + "agentId": "a1", + "title": "Hello", + "messageCount": 3, + "createdAt": "2026-01-01T00:00:00Z", + "updatedAt": "2026-01-01T00:00:00Z", + }}, + "pagination": map[string]any{"page": 1, "limit": 10, "totalCount": 1, "totalPages": 1}, + }), + ) + defer r.Verify(t) + + f, out := test.NewFactory(true, &r, nil, "") + cmd := NewListCmd(f, nil) + _, err := test.Execute(cmd, "a1", out) + require.NoError(t, err) + assert.Contains(t, out.String(), "conv-1") + assert.Contains(t, out.String(), "Hello") + assert.Contains(t, out.String(), "3") +} diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index 3216c91c..cc0ef154 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -9,6 +9,7 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" + "github.com/algolia/cli/api/agentstudio" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" @@ -23,6 +24,7 @@ func New(appVersion string, cfg config.IConfig) *cmdutil.Factory { f.IOStreams = ioStreams(f) f.SearchClient = searchClient(f, appVersion) f.CrawlerClient = crawlerClient(f) + f.AgentStudioClient = agentStudioClient(f) return f } @@ -85,6 +87,21 @@ func crawlerClient(f *cmdutil.Factory) func() (*crawler.Client, error) { } } +func agentStudioClient(f *cmdutil.Factory) func() (*agentstudio.Client, error) { + return func() (*agentstudio.Client, error) { + appID, err := f.Config.Profile().GetApplicationID() + if err != nil { + return nil, err + } + apiKey, err := f.Config.Profile().GetAPIKey() + if err != nil { + return nil, err + } + + return agentstudio.NewClient(appID, apiKey), nil + } +} + // getUserAgentInfo returns the standard user agent info plus Algolia CLI func getUserAgentInfo(appID string, apiKey string, appVersion string) (string, error) { client, err := search.NewClient(appID, apiKey) diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 1bf69e25..550ffb4b 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -21,6 +21,7 @@ import ( "github.com/algolia/cli/internal/update" "github.com/algolia/cli/pkg/auth" + "github.com/algolia/cli/pkg/cmd/agentstudio" "github.com/algolia/cli/pkg/cmd/apikeys" "github.com/algolia/cli/pkg/cmd/application" authcmd "github.com/algolia/cli/pkg/cmd/auth" @@ -115,6 +116,7 @@ func NewRootCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(dictionary.NewDictionaryCmd(f)) cmd.AddCommand(events.NewEventsCmd(f)) cmd.AddCommand(crawler.NewCrawlersCmd(f)) + cmd.AddCommand(agentstudio.NewAgentStudioCmd(f)) return cmd } diff --git a/pkg/cmdutil/agent_studio_flags.go b/pkg/cmdutil/agent_studio_flags.go new file mode 100644 index 00000000..b24096f1 --- /dev/null +++ b/pkg/cmdutil/agent_studio_flags.go @@ -0,0 +1,54 @@ +// This file is generated; DO NOT EDIT. + +package cmdutil + +import ( + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" +) + +var AgentCompletionRequest = []string{ + "algolia", + "configuration", + "id", + "messages", + "toolApprovals", +} + +var AgentConfigCreate = []string{ + "config", + "description", + "instructions", + "model", + "name", + "providerId", + "systemPrompt", + "templateType", + "tools", +} + +func AddAgentCompletionRequestFlags(cmd *cobra.Command) { + algolia := NewJSONVar([]string{}...) + cmd.Flags().Var(algolia, "algolia", heredoc.Doc(`.`)) + configuration := NewJSONVar([]string{}...) + cmd.Flags().Var(configuration, "configuration", heredoc.Doc(`Dynamic configuration for testing agents.`)) + cmd.Flags().String("id", "", heredoc.Doc(`.`)) + messages := NewJSONVar([]string{"array", "array"}...) + cmd.Flags().Var(messages, "messages", heredoc.Doc(`.`)) + toolApprovals := NewJSONVar([]string{}...) + cmd.Flags().Var(toolApprovals, "toolApprovals", heredoc.Doc(`.`)) +} + +func AddAgentConfigCreateFlags(cmd *cobra.Command) { + config := NewJSONVar([]string{}...) + cmd.Flags().Var(config, "config", heredoc.Doc(`.`)) + cmd.Flags().String("description", "", heredoc.Doc(`.`)) + cmd.Flags().String("instructions", "", heredoc.Doc(`The agent prompt: defines the agent's role, tone, and goals. Guides how it answers using the provided context. Corresponds to the 'Agent prompt' field in the dashboard.`)) + cmd.Flags().String("model", "", heredoc.Doc(`.`)) + cmd.Flags().String("name", "", heredoc.Doc(`.`)) + cmd.Flags().String("providerId", "", heredoc.Doc(`.`)) + cmd.Flags().String("systemPrompt", "", heredoc.Doc(`.`)) + cmd.Flags().String("templateType", "", heredoc.Doc(`.`)) + tools := NewJSONVar([]string{}...) + cmd.Flags().Var(tools, "tools", heredoc.Doc(`.`)) +} diff --git a/pkg/cmdutil/factory.go b/pkg/cmdutil/factory.go index fb88f3b5..dfa1a25d 100644 --- a/pkg/cmdutil/factory.go +++ b/pkg/cmdutil/factory.go @@ -7,16 +7,18 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/cli/api/agentstudio" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/config" "github.com/algolia/cli/pkg/iostreams" ) type Factory struct { - IOStreams *iostreams.IOStreams - Config config.IConfig - SearchClient func() (*search.APIClient, error) - CrawlerClient func() (*crawler.Client, error) + IOStreams *iostreams.IOStreams + Config config.IConfig + SearchClient func() (*search.APIClient, error) + CrawlerClient func() (*crawler.Client, error) + AgentStudioClient func() (*agentstudio.Client, error) ExecutableName string } diff --git a/pkg/cmdutil/json_merge.go b/pkg/cmdutil/json_merge.go new file mode 100644 index 00000000..9fae5f0b --- /dev/null +++ b/pkg/cmdutil/json_merge.go @@ -0,0 +1,43 @@ +package cmdutil + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + + "github.com/algolia/cli/pkg/iostreams" +) + +// MergeFileAndFlagsInto reads an optional JSON `file` (path or "-" for stdin), +// then overlays the named cobra flag values on top, then unmarshals the +// merged map into out. Flag values win over file values for the same key. +// +// Used by commands that accept both `-F file.json` and a generated flag set +// derived from an OpenAPI schema (see e.g. AgentConfigCreate flags). +func MergeFileAndFlagsInto(io *iostreams.IOStreams, file string, cmd *cobra.Command, flagNames []string, out any) error { + merged := map[string]any{} + if file != "" { + b, err := ReadFile(file, io.In) + if err != nil { + return err + } + if err := json.Unmarshal(b, &merged); err != nil { + return fmt.Errorf("parsing %s: %w", file, err) + } + } + + flagVals, err := FlagValuesMap(cmd.Flags(), flagNames...) + if err != nil { + return err + } + for k, v := range flagVals { + merged[k] = v + } + + tmp, err := json.Marshal(merged) + if err != nil { + return err + } + return json.Unmarshal(tmp, out) +} diff --git a/pkg/cmdutil/spec_flags.go b/pkg/cmdutil/spec_flags.go index 7df3005c..caafb808 100644 --- a/pkg/cmdutil/spec_flags.go +++ b/pkg/cmdutil/spec_flags.go @@ -341,9 +341,12 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/hitsPerPage/`)) cmd.Flags().Var(ignorePlurals, "ignorePlurals", heredoc.Doc(`Treat singular, plurals, and other forms of declensions as equivalent. See: https://www.algolia.com/doc/api-reference/api-parameters/ignorePlurals/`)) cmd.Flags().SetAnnotation("ignorePlurals", "Categories", []string{"Languages"}) - insideBoundingBox := NewJSONVar([]string{"string", "null", "array"}...) + insideBoundingBox := NewJSONVar([]string{"string", "", "array"}...) cmd.Flags().Var(insideBoundingBox, "insideBoundingBox", heredoc.Doc(`. See: https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/`)) + insidePolygon := NewJSONVar([]string{}...) + cmd.Flags().Var(insidePolygon, "insidePolygon", heredoc.Doc(`Coordinates of a polygon in which to search. +See: https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/`)) cmd.Flags().SetAnnotation("insidePolygon", "Categories", []string{"Geo-Search"}) cmd.Flags().Int("length", 0, heredoc.Doc(`Number of hits to retrieve (used in combination with offset). See: https://www.algolia.com/doc/api-reference/api-parameters/length/`)) @@ -380,7 +383,7 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/offset/`)) cmd.Flags().Var(optionalFilters, "optionalFilters", heredoc.Doc(`Filters to promote or demote records in the search results. See: https://www.algolia.com/doc/api-reference/api-parameters/optionalFilters/`)) cmd.Flags().SetAnnotation("optionalFilters", "Categories", []string{"Filtering"}) - optionalWords := NewJSONVar([]string{"string", "null", "array"}...) + optionalWords := NewJSONVar([]string{"string", "", "array"}...) cmd.Flags().Var(optionalWords, "optionalWords", heredoc.Doc(`Words that should be considered optional when found in the query. See: https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/`)) cmd.Flags().Int("page", 0, heredoc.Doc(`Page of search results to retrieve. @@ -404,7 +407,7 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/queryType/`)) cmd.Flags().StringSlice("ranking", []string{"typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom"}, heredoc.Doc(`Determines the order in which Algolia returns your results. See: https://www.algolia.com/doc/api-reference/api-parameters/ranking/`)) cmd.Flags().SetAnnotation("ranking", "Categories", []string{"Ranking"}) - reRankingApplyFilter := NewJSONVar([]string{"", "null"}...) + reRankingApplyFilter := NewJSONVar([]string{"", ""}...) cmd.Flags().Var(reRankingApplyFilter, "reRankingApplyFilter", heredoc.Doc(`.`)) cmd.Flags().Int("relevancyStrictness", 100, heredoc.Doc(`Relevancy threshold below which less relevant results aren't included in the results. See: https://www.algolia.com/doc/api-reference/api-parameters/relevancyStrictness/`)) @@ -480,9 +483,12 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/facetFilters/`)) cmd.Flags().String("filters", "", heredoc.Doc(`Filter expression to only include items that match the filter criteria in the response. See: https://www.algolia.com/doc/api-reference/api-parameters/filters/`)) cmd.Flags().SetAnnotation("filters", "Categories", []string{"Filtering"}) - insideBoundingBox := NewJSONVar([]string{"string", "null", "array"}...) + insideBoundingBox := NewJSONVar([]string{"string", "", "array"}...) cmd.Flags().Var(insideBoundingBox, "insideBoundingBox", heredoc.Doc(`. See: https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/`)) + insidePolygon := NewJSONVar([]string{}...) + cmd.Flags().Var(insidePolygon, "insidePolygon", heredoc.Doc(`Coordinates of a polygon in which to search. +See: https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/`)) cmd.Flags().SetAnnotation("insidePolygon", "Categories", []string{"Geo-Search"}) numericFilters := NewJSONVar([]string{"array", "string"}...) cmd.Flags().Var(numericFilters, "numericFilters", heredoc.Doc(`Filter by numeric facets. @@ -615,7 +621,7 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/mode/`)) cmd.Flags().StringSlice("numericAttributesForFiltering", []string{}, heredoc.Doc(`Numeric attributes that can be used as numerical filters. See: https://www.algolia.com/doc/api-reference/api-parameters/numericAttributesForFiltering/`)) cmd.Flags().SetAnnotation("numericAttributesForFiltering", "Categories", []string{"Performance"}) - optionalWords := NewJSONVar([]string{"string", "null", "array"}...) + optionalWords := NewJSONVar([]string{"string", "", "array"}...) cmd.Flags().Var(optionalWords, "optionalWords", heredoc.Doc(`Words that should be considered optional when found in the query. See: https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/`)) cmd.Flags().Int("paginationLimitedTo", 1000, heredoc.Doc(`Maximum number of search results that can be obtained through pagination. @@ -629,7 +635,7 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/queryType/`)) cmd.Flags().StringSlice("ranking", []string{"typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom"}, heredoc.Doc(`Determines the order in which Algolia returns your results. See: https://www.algolia.com/doc/api-reference/api-parameters/ranking/`)) cmd.Flags().SetAnnotation("ranking", "Categories", []string{"Ranking"}) - reRankingApplyFilter := NewJSONVar([]string{"", "null"}...) + reRankingApplyFilter := NewJSONVar([]string{"", ""}...) cmd.Flags().Var(reRankingApplyFilter, "reRankingApplyFilter", heredoc.Doc(`.`)) cmd.Flags().Int("relevancyStrictness", 100, heredoc.Doc(`Relevancy threshold below which less relevant results aren't included in the results. See: https://www.algolia.com/doc/api-reference/api-parameters/relevancyStrictness/`)) @@ -789,9 +795,12 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/hitsPerPage/`)) cmd.Flags().Var(ignorePlurals, "ignorePlurals", heredoc.Doc(`Treat singular, plurals, and other forms of declensions as equivalent. See: https://www.algolia.com/doc/api-reference/api-parameters/ignorePlurals/`)) cmd.Flags().SetAnnotation("ignorePlurals", "Categories", []string{"Languages"}) - insideBoundingBox := NewJSONVar([]string{"string", "null", "array"}...) + insideBoundingBox := NewJSONVar([]string{"string", "", "array"}...) cmd.Flags().Var(insideBoundingBox, "insideBoundingBox", heredoc.Doc(`. See: https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/`)) + insidePolygon := NewJSONVar([]string{}...) + cmd.Flags().Var(insidePolygon, "insidePolygon", heredoc.Doc(`Coordinates of a polygon in which to search. +See: https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/`)) cmd.Flags().SetAnnotation("insidePolygon", "Categories", []string{"Geo-Search"}) cmd.Flags().Int("length", 0, heredoc.Doc(`Number of hits to retrieve (used in combination with offset). See: https://www.algolia.com/doc/api-reference/api-parameters/length/`)) @@ -828,7 +837,7 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/offset/`)) cmd.Flags().Var(optionalFilters, "optionalFilters", heredoc.Doc(`Filters to promote or demote records in the search results. See: https://www.algolia.com/doc/api-reference/api-parameters/optionalFilters/`)) cmd.Flags().SetAnnotation("optionalFilters", "Categories", []string{"Filtering"}) - optionalWords := NewJSONVar([]string{"string", "null", "array"}...) + optionalWords := NewJSONVar([]string{"string", "", "array"}...) cmd.Flags().Var(optionalWords, "optionalWords", heredoc.Doc(`Words that should be considered optional when found in the query. See: https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/`)) cmd.Flags().Int("page", 0, heredoc.Doc(`Page of search results to retrieve. @@ -852,7 +861,7 @@ See: https://www.algolia.com/doc/api-reference/api-parameters/queryType/`)) cmd.Flags().StringSlice("ranking", []string{"typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom"}, heredoc.Doc(`Determines the order in which Algolia returns your results. See: https://www.algolia.com/doc/api-reference/api-parameters/ranking/`)) cmd.Flags().SetAnnotation("ranking", "Categories", []string{"Ranking"}) - reRankingApplyFilter := NewJSONVar([]string{"", "null"}...) + reRankingApplyFilter := NewJSONVar([]string{"", ""}...) cmd.Flags().Var(reRankingApplyFilter, "reRankingApplyFilter", heredoc.Doc(`.`)) cmd.Flags().Int("relevancyStrictness", 100, heredoc.Doc(`Relevancy threshold below which less relevant results aren't included in the results. See: https://www.algolia.com/doc/api-reference/api-parameters/relevancyStrictness/`)) diff --git a/pkg/gen/gen_flags.go b/pkg/gen/gen_flags.go index 1601273b..24187f41 100644 --- a/pkg/gen/gen_flags.go +++ b/pkg/gen/gen_flags.go @@ -5,10 +5,9 @@ package main import ( "bytes" - "encoding/json" "fmt" "go/format" - "io/ioutil" + "os" "regexp" "strings" "text/template" @@ -35,30 +34,44 @@ type SpecFlag struct { } const ( - searchSpecFile = "../../../api/specs/search.yml" - pathTemplate = "../../gen/flags.go.tpl" - pathName = "flags.go.tpl" - pathOutput = "../../cmdutil/spec_flags.go" + pathTemplate = "../../gen/flags.go.tpl" + pathName = "flags.go.tpl" ) -func main() { - // This is the script that generates the `flags.go` file from the - // OpenAPI spec file. - - specNames := []string{ - "searchParamsObject", - "browseParamsObject", - "indexSettings", - "deleteByParams", - } - templateData, err := getTemplateData(specNames) - if err != nil { - panic(err) - } +// SpecConfig describes a single OpenAPI document and which schemas to flatten +// into Cobra flag bindings. +type SpecConfig struct { + File string // path to the spec, relative to pkg/gen + Schemas []string // top-level component schema names + DocLinks bool // append Algolia search-API doc links to descriptions + OutputPath string // generated Go file (relative to pkg/gen) +} - // Load the template with a custom function map +var specs = []SpecConfig{ + { + File: "../../../api/specs/search.yml", + Schemas: []string{ + "searchParamsObject", + "browseParamsObject", + "indexSettings", + "deleteByParams", + }, + DocLinks: true, + OutputPath: "../../cmdutil/spec_flags.go", + }, + { + File: "../../../api/specs/agent-studio.json", + Schemas: []string{ + "AgentConfigCreate", + "AgentCompletionRequest", + }, + DocLinks: false, + OutputPath: "../../cmdutil/agent_studio_flags.go", + }, +} + +func main() { tmpl := template.Must(template. - // Note that the template name MUST match the file name New(pathName). Funcs(template.FuncMap{ "capitalize": func(s string) string { @@ -67,24 +80,26 @@ func main() { }). ParseFiles(pathTemplate)) - // Execute the template - var result bytes.Buffer - err = tmpl.Execute(&result, templateData) - if err != nil { - panic(err) - } + for _, spec := range specs { + data, err := getTemplateData(spec) + if err != nil { + panic(fmt.Errorf("loading %s: %w", spec.File, err)) + } - // Format the output of the template execution - formatted, err := format.Source(result.Bytes()) - if err != nil { - panic(err) - } + var result bytes.Buffer + if err := tmpl.Execute(&result, data); err != nil { + panic(err) + } - // Write the formatted source code to disk - fmt.Printf("writing %s\n", pathOutput) - err = ioutil.WriteFile(pathOutput, formatted, 0o644) - if err != nil { - panic(err) + formatted, err := format.Source(result.Bytes()) + if err != nil { + panic(err) + } + + fmt.Printf("writing %s\n", spec.OutputPath) + if err := os.WriteFile(spec.OutputPath, formatted, 0o600); err != nil { + panic(err) + } } } @@ -130,76 +145,127 @@ func loadSpecs(specFile, specName string) (map[string]*openapi3.Schema, error) { return loadProperties(schemaRef), nil } -// This is the function that loads the OpenAPI 3.0 spec file and -// returns the data for the template. -func getTemplateData(specNames []string) (TemplateData, error) { - data := &TemplateData{ - SpecFlags: make(map[string]*SpecFlags), - } - for _, specName := range specNames { - specParams, err := loadSpecs(searchSpecFile, specName) +// getTemplateData loads all flags for a single SpecConfig. +func getTemplateData(spec SpecConfig) (TemplateData, error) { + data := TemplateData{SpecFlags: make(map[string]*SpecFlags)} + for _, name := range spec.Schemas { + params, err := loadSpecs(spec.File, name) if err != nil { - return *data, err + return data, err } - data.SpecFlags[specName] = getFlags(specParams) + data.SpecFlags[name] = getFlags(params, spec.DocLinks) } - return *data, nil + return data, nil } // getFlags returns the flags for the given spec. -func getFlags(params map[string]*openapi3.Schema) *SpecFlags { - flags := &SpecFlags{ - Flags: make(map[string]*SpecFlag), - } +func getFlags(params map[string]*openapi3.Schema, withDocLinks bool) *SpecFlags { + flags := &SpecFlags{Flags: make(map[string]*SpecFlag)} for name, param := range params { - flags.Flags[name] = getFlag(name, param) + flags.Flags[name] = getFlag(name, param, withDocLinks) } return flags } -// GetGoType returns the Go type for the given OpenAPI 3.0 schema. +// unwrapNullable handles OpenAPI 3.1 `anyOf: [T, {type: null}]` nullability. +// When a schema has no Type set but its AnyOf contains exactly one non-null +// branch and one explicit null branch, return the non-null branch. Otherwise +// return the input unchanged. +func unwrapNullable(s *openapi3.Schema) *openapi3.Schema { + if s == nil || s.Type != nil || len(s.AnyOf) == 0 { + return s + } + var nonNull *openapi3.Schema + for _, branch := range s.AnyOf { + if branch == nil || branch.Value == nil { + continue + } + if branch.Value.Type != nil && branch.Value.Type.Is("null") { + continue + } + if nonNull != nil { + return s // more than one non-null branch — leave as-is + } + nonNull = branch.Value + } + if nonNull == nil { + return s + } + return nonNull +} + +// schemaTypeString returns the primary OpenAPI type for a schema, accounting +// for the v0.135 *Types representation. Returns "" if no concrete type is set. +func schemaTypeString(s *openapi3.Schema) string { + if s == nil || s.Type == nil { + return "" + } + for _, t := range *s.Type { + if t != "" && t != "null" { + return t + } + } + return "" +} + +// GetGoType returns the Go type for the given OpenAPI 3.0/3.1 schema. func GetGoType(param *openapi3.Schema) string { - SpecTypeGoType := map[string]string{ + param = unwrapNullable(param) + specTypeGoType := map[string]string{ "string": "string", "integer": "int", "number": "float64", "boolean": "bool", } - if param.Type == "array" { + t := schemaTypeString(param) + if t == "array" && param.Items != nil && param.Items.Value != nil { return "[]" + GetGoType(param.Items.Value) } - return SpecTypeGoType[param.Type] + return specTypeGoType[t] } // getFlag returns the flag for the given parameter. -func getFlag(name string, param *openapi3.Schema) *SpecFlag { +func getFlag(name string, param *openapi3.Schema, withDocLinks bool) *SpecFlag { + param = unwrapNullable(param) + t := schemaTypeString(param) + subType := "" - if param.Type == "array" { - subType = param.Items.Value.Type - } else { - subType = "" + if t == "array" && param.Items != nil && param.Items.Value != nil { + subType = schemaTypeString(unwrapNullable(param.Items.Value)) + } + + // Arrays of unions / objects can't be modeled as typed slices; fall through + // to the JSONVar branch in the template by clearing the type. + if t == "array" && subType != "string" && subType != "integer" && subType != "number" { + t = "" } var categories []string - if param.ExtensionProps.Extensions["x-categories"] != nil { - json.Unmarshal( - param.ExtensionProps.Extensions["x-categories"].(json.RawMessage), - &categories, - ) + if raw, ok := param.Extensions["x-categories"]; ok { + switch v := raw.(type) { + case []any: + for _, item := range v { + if s, ok := item.(string); ok { + categories = append(categories, s) + } + } + case []string: + categories = append(categories, v...) + } } flag := &SpecFlag{ Def: param.Default, - Type: param.Type, + Type: t, GoType: GetGoType(param), - Usage: getDescription(name, param), + Usage: getDescription(name, param, withDocLinks), SubType: subType, Categories: categories, } if param.OneOf != nil { for _, oneOf := range param.OneOf { - flag.OneOf = append(flag.OneOf, oneOf.Value.Type) + flag.OneOf = append(flag.OneOf, schemaTypeString(unwrapNullable(oneOf.Value))) } } @@ -236,12 +302,11 @@ func replaceMarkdownLinks(text string) string { // getDescription returns the short description for the given parameter. // It's the first sentence of the parameter description followed by possible values if it's an enum, -// followed by a link to the API param reference page -func getDescription(name string, param *openapi3.Schema) string { - withLink := true +// followed by a link to the API param reference page when withDocLinks is true. +func getDescription(name string, param *openapi3.Schema, withDocLinks bool) string { // These params don't have an API reference page if name == "semanticSearch" || name == "cursor" || name == "reRankingApplyFilter" { - withLink = false + withDocLinks = false } description := shortDescription(param.Description) @@ -250,13 +315,13 @@ func getDescription(name string, param *openapi3.Schema) string { if param.Enum != nil { choices := make([]string, len(param.Enum)) for i, e := range param.Enum { - choices[i] = e.(string) + choices[i] = fmt.Sprintf("%v", e) } description = fmt.Sprintf("%s One of: %v.", description, strings.Join(choices, ", ")) } // Add link to the API param reference page - if withLink { + if withDocLinks { link := fmt.Sprintf("https://www.algolia.com/doc/api-reference/api-parameters/%s/", name) description = fmt.Sprintf("%s\nSee: %s", description, link) } diff --git a/test/helpers.go b/test/helpers.go index 039335ad..7a6c7434 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -10,6 +10,7 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" + "github.com/algolia/cli/api/agentstudio" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" @@ -85,6 +86,11 @@ func NewFactory( Transport: r, }), nil } + f.AgentStudioClient = func() (*agentstudio.Client, error) { + c := agentstudio.NewClientWithHTTPClient("APP", "KEY", &http.Client{Transport: r}) + c.BaseURL = "http://test.local/" + return c, nil + } } if cfg != nil {