squidcloud

Squid Cloud Python client SDK.

Usage::

from squidcloud import Squid

squid = Squid(
    app_id="my-app",
    api_key="my-api-key",
    region="us-east-1.aws",
    environment_id="dev",
)

# AI agents
response = await squid.ai().agent("my-agent").ask("Hello!")

# Execute backend function
result = await squid.execute_function("MyService:greet", "World")

# Web utilities
content = await squid.web().get_url_content("https://example.com")
  1"""Squid Cloud Python client SDK.
  2
  3Usage::
  4
  5    from squidcloud import Squid
  6
  7    squid = Squid(
  8        app_id="my-app",
  9        api_key="my-api-key",
 10        region="us-east-1.aws",
 11        environment_id="dev",
 12    )
 13
 14    # AI agents
 15    response = await squid.ai().agent("my-agent").ask("Hello!")
 16
 17    # Execute backend function
 18    result = await squid.execute_function("MyService:greet", "World")
 19
 20    # Web utilities
 21    content = await squid.web().get_url_content("https://example.com")
 22"""
 23
 24from squidcloud.ai import (
 25    AgentClient,
 26    AiClient,
 27    AudioClient,
 28    ImageClient,
 29    KnowledgeBaseClient,
 30)
 31from squidcloud.client import Squid
 32from squidcloud.extraction import ExtractionClient
 33from squidcloud.http import SquidHttpError
 34from squidcloud.matchmaking import MatchmakingClient
 35from squidcloud.types import (
 36    AiAgent,
 37    AiAgentExecutionPlanOptions,
 38    AiAgentMcpServerConfig,
 39    AiAgentMemoryOptions,
 40    AiAgentResponseFormat,
 41    AiAudioCreateSpeechOptions,
 42    AiChatModelSelection,
 43    AiChatOptions,
 44    AiChatPromptQuotas,
 45    AiConnectedAgentMetadata,
 46    AiConnectedIntegrationMetadata,
 47    AiConnectedKnowledgeBaseMetadata,
 48    AiContextFileOptions,
 49    AiContextTextOptions,
 50    AiEmbeddingsModelSelection,
 51    AiFileUrl,
 52    AiFunctionAttributes,
 53    AiFunctionMetadata,
 54    AiFunctionParam,
 55    AiKnowledgeBase,
 56    AiKnowledgeBaseGraphConceptsConfig,
 57    AiKnowledgeBaseGraphConfig,
 58    AiKnowledgeBaseGraphFilter,
 59    AiKnowledgeBaseGraphPathFacetConfig,
 60    AiKnowledgeBaseGraphSearchOptions,
 61    AiKnowledgeBaseMetadataField,
 62    AiPiiOptions,
 63    AiQueryAnalyzeResultsOptions,
 64    AiQueryCollectionsSelectionRunMode,
 65    AiQueryGenerateQueryOptions,
 66    AiQueryOptions,
 67    AiQuerySelectCollectionsOptions,
 68    AiQueryValidateWithAiOptions,
 69    AiSessionContext,
 70    AiStructuredOutputFormat,
 71    ContextRequest,
 72    CreatePdfDimensionsOptions,
 73    CreatePdfFormatOptions,
 74    CreatePdfOutputOptions,
 75    EmbeddingModelIdSpec,
 76    ExtractDataFromDocumentOptions,
 77    FileContextRequest,
 78    FluxOptions,
 79    GptImageOptions,
 80    GuardrailsOptions,
 81    ImageGenerateOptions,
 82    IntegrationEmbeddingModelSpec,
 83    IntegrationModelSpec,
 84    KnowledgeBaseGraphQueryOp,
 85    KnowledgeBaseSearchOptions,
 86    MmCategory,
 87    MmEntity,
 88    MmFindMatchesOptions,
 89    MmListEntitiesOptions,
 90    ModelIdSpec,
 91    StableDiffusionOptions,
 92    TextContextRequest,
 93    UpsertAgentOptions,
 94    VectorDbType,
 95    WebAiSearchResponse,
 96    WebShortUrlBulkResponse,
 97    WebShortUrlResponse,
 98)
 99from squidcloud.web import WebClient
100
101__all__ = [
102    "AgentClient",
103    "AiAgent",
104    "AiAgentExecutionPlanOptions",
105    "AiAgentMcpServerConfig",
106    "AiAgentMemoryOptions",
107    "AiAgentResponseFormat",
108    "AiAudioCreateSpeechOptions",
109    "AiChatModelSelection",
110    # AI Chat
111    "AiChatOptions",
112    "AiChatPromptQuotas",
113    "AiClient",
114    "AiConnectedAgentMetadata",
115    "AiConnectedIntegrationMetadata",
116    "AiConnectedKnowledgeBaseMetadata",
117    "AiContextFileOptions",
118    "AiContextTextOptions",
119    "AiEmbeddingsModelSelection",
120    "AiFileUrl",
121    "AiFunctionAttributes",
122    "AiFunctionMetadata",
123    "AiFunctionParam",
124    # Knowledge Base
125    "AiKnowledgeBase",
126    "AiKnowledgeBaseGraphConceptsConfig",
127    "AiKnowledgeBaseGraphConfig",
128    "AiKnowledgeBaseGraphFilter",
129    "AiKnowledgeBaseGraphPathFacetConfig",
130    "AiKnowledgeBaseGraphSearchOptions",
131    "AiKnowledgeBaseMetadataField",
132    "AiPiiOptions",
133    # AI Query
134    "AiQueryAnalyzeResultsOptions",
135    "AiQueryCollectionsSelectionRunMode",
136    "AiQueryGenerateQueryOptions",
137    "AiQueryOptions",
138    "AiQuerySelectCollectionsOptions",
139    "AiQueryValidateWithAiOptions",
140    "AiSessionContext",
141    "AiStructuredOutputFormat",
142    "AudioClient",
143    "ContextRequest",
144    "CreatePdfDimensionsOptions",
145    "CreatePdfFormatOptions",
146    # Extraction
147    "CreatePdfOutputOptions",
148    "EmbeddingModelIdSpec",
149    "ExtractDataFromDocumentOptions",
150    "ExtractionClient",
151    "FileContextRequest",
152    "FluxOptions",
153    "GptImageOptions",
154    "GuardrailsOptions",
155    # Image
156    "ImageClient",
157    "ImageGenerateOptions",
158    "IntegrationEmbeddingModelSpec",
159    "IntegrationModelSpec",
160    "KnowledgeBaseClient",
161    "KnowledgeBaseGraphQueryOp",
162    "KnowledgeBaseSearchOptions",
163    # Matchmaking
164    "MatchmakingClient",
165    "MmCategory",
166    "MmEntity",
167    "MmFindMatchesOptions",
168    "MmListEntitiesOptions",
169    "ModelIdSpec",
170    "Squid",
171    "SquidHttpError",
172    "StableDiffusionOptions",
173    "TextContextRequest",
174    "UpsertAgentOptions",
175    "VectorDbType",
176    # Web
177    "WebAiSearchResponse",
178    "WebClient",
179    "WebShortUrlBulkResponse",
180    "WebShortUrlResponse",
181]
class AgentClient:
168class AgentClient:
169    """Operations on a single AI agent.
170
171    Provides methods for chatting with an agent, managing its configuration,
172    updating guardrails, and working with revisions.
173
174    Obtained via :meth:`AiClient.agent`.
175
176    Example::
177
178        agent = squid.ai().agent("my-agent")
179        response = await agent.ask("What is the weather?", options={"temperature": 0.7})
180        await agent.update_instructions("Always respond in haiku format.")
181    """
182
183    def __init__(self, http: HttpTransport, agent_id: str) -> None:
184        self._http = http
185        self._agent_id = agent_id
186
187    # --- Chat ---
188
189    async def ask(
190        self,
191        prompt: str,
192        options: AiChatOptions | None = None,
193    ) -> str:
194        """Ask the agent a question and get a text response.
195
196        Args:
197            prompt: The user's question or instruction.
198            options: Chat options controlling model, temperature, memory,
199                functions, guardrails, and more. See :class:`AiChatOptions`.
200
201        Returns:
202            The agent's response as a string.
203
204        Raises:
205            SquidHttpError: If the agent is not found or the request fails.
206
207        Example::
208
209            response = await agent.ask(
210                "Summarize this document",
211                options={
212                    "model": "gemini-3.8-flash",
213                    "temperature": 0.3,
214                    "memoryOptions": {"memoryId": "session-1", "memoryMode": "read-write"},
215                },
216            )
217        """
218        result = await self._http.post(
219            "squid-api/v1/ai/agent/ask",
220            {"agentId": self._agent_id, "prompt": prompt, "options": options or {}},
221        )
222        return result.get("responseString", "") if result else ""
223
224    async def ask_with_annotations(
225        self,
226        prompt: str,
227        options: AiChatOptions | None = None,
228    ) -> dict:
229        """Ask the agent and get a response with annotations.
230
231        Annotations include file references, citations, and other metadata
232        the agent may attach to its response.
233
234        Args:
235            prompt: The user's question or instruction.
236            options: Chat options. See :class:`AiChatOptions`.
237
238        Returns:
239            A dict with:
240                - ``responseString`` (str): The text response.
241                - ``annotations`` (dict): Annotation metadata keyed by ID.
242
243        Example::
244
245            result = await agent.ask_with_annotations("Find relevant docs")
246            print(result["responseString"])
247            for ann_id, ann in result.get("annotations", {}).items():
248                print(f"  Annotation: {ann}")
249        """
250        return await self._http.post(
251            "squid-api/v1/ai/agent/askWithAnnotations",
252            {"agentId": self._agent_id, "prompt": prompt, "options": options or {}},
253        )
254
255    # --- Management ---
256
257    async def get(self) -> dict | None:
258        """Get the agent's configuration.
259
260        Returns:
261            An ``AiAgent`` dict with keys: ``id``, ``createdAt``, ``updatedAt``,
262            ``description``, ``isPublic``, ``auditLog``, ``auditLogFullContext``,
263            ``options``, ``apiKey``.
264            Returns ``None`` if the agent does not exist.
265        """
266        return await self._http.get(f"squid-api/v1/ai/agent/get/{self._agent_id}")
267
268    async def upsert(
269        self,
270        *,
271        description: str | None = None,
272        is_public: bool | None = None,
273        audit_log: bool | None = None,
274        audit_log_full_context: bool | None = None,
275        api_key: str | None = None,
276        mcp_server: AiAgentMcpServerConfig | None = None,
277        options: AiChatOptions | None = None,
278    ) -> None:
279        """Create or update the agent.
280
281        If the agent does not exist, it is created. If it exists, the provided
282        fields are updated (fields set to ``None`` are left unchanged).
283
284        Args:
285            description: A description of the agent's purpose or capabilities.
286            is_public: Whether the agent is publicly accessible (default ``False``).
287            audit_log: Enable audit logging for the agent's activities.
288            audit_log_full_context: Record the full agent context (system
289                instructions and retrieved knowledge-base content) in the audit
290                log; requires ``audit_log``.
291            api_key: Optional API key used specifically for this agent.
292            mcp_server: Configuration for exposing the agent as an MCP server
293                at ``/mcp/<agentId>``. See :class:`AiAgentMcpServerConfig`.
294            options: Default chat options applied to every request unless
295                overridden per-call. See :class:`AiChatOptions`.
296
297        Example::
298
299            await agent.upsert(
300                description="Customer support agent",
301                options={"model": "gemini-3.8-flash", "temperature": 0.5},
302            )
303        """
304        body: dict[str, Any] = {"id": self._agent_id}
305        if description is not None:
306            body["description"] = description
307        if is_public is not None:
308            body["isPublic"] = is_public
309        if audit_log is not None:
310            body["auditLog"] = audit_log
311        if audit_log_full_context is not None:
312            body["auditLogFullContext"] = audit_log_full_context
313        if api_key is not None:
314            body["apiKey"] = api_key
315        if mcp_server is not None:
316            body["mcpServer"] = mcp_server
317        if options is not None:
318            body["options"] = options
319        await self._http.post("squid-api/v1/ai/agent/upsert", body)
320
321    async def delete(self) -> None:
322        """Delete the agent permanently."""
323        await self._http.post("squid-api/v1/ai/agent/delete", {"agentId": self._agent_id})
324
325    async def update_instructions(self, instructions: str) -> None:
326        """Update the agent's system instructions.
327
328        Args:
329            instructions: The new system prompt / instructions text.
330        """
331        await self._http.post(
332            "squid-api/v1/ai/agent/updateInstructions",
333            {"agentId": self._agent_id, "instructions": instructions},
334        )
335
336    async def update_model(self, model: AiChatModelSelection) -> None:
337        """Update the agent's default LLM model.
338
339        Args:
340            model: A model name string (e.g., ``'gemini-3.8-flash'``) or an
341                ``IntegrationModelSpec`` dict (``{'integrationId': str, 'model': str}``).
342        """
343        await self._http.post(
344            "squid-api/v1/ai/agent/updateModel",
345            {"agentId": self._agent_id, "model": model},
346        )
347
348    async def update_connected_agents(
349        self, connected_agents: list[AiConnectedAgentMetadata]
350    ) -> None:
351        """Update the list of connected agents.
352
353        Connected agents can be called by this agent during conversations.
354
355        Args:
356            connected_agents: List of ``{'agentId': str, 'description': str}`` dicts.
357        """
358        await self._http.post(
359            "squid-api/v1/ai/agent/updateConnectedAgents",
360            {"agentId": self._agent_id, "connectedAgents": connected_agents},
361        )
362
363    async def update_guardrails(self, guardrails: GuardrailsOptions) -> None:
364        """Update the agent's guardrail settings.
365
366        Args:
367            guardrails: A :class:`GuardrailsOptions` dict with optional keys:
368                ``custom``, ``disablePii``, ``professionalTone``,
369                ``offTopicAnswers``, ``disableProfanity``.
370
371        Example::
372
373            await agent.update_guardrails(
374                {
375                    "professionalTone": True,
376                    "disableProfanity": True,
377                }
378            )
379        """
380        await self._http.post(
381            "squid-api/v1/ai/agent/updateGuardrails",
382            {"agentId": self._agent_id, "guardrails": guardrails},
383        )
384
385    async def update_custom_guardrails(self, custom_guardrail: str) -> None:
386        """Update the custom guardrail instruction text.
387
388        Args:
389            custom_guardrail: Free-form guardrail instruction string.
390        """
391        await self._http.post(
392            "squid-api/v1/ai/agent/updateCustomGuardrails",
393            {"agentId": self._agent_id, "customGuardrail": custom_guardrail},
394        )
395
396    async def delete_custom_guardrails(self) -> None:
397        """Delete the custom guardrail, reverting to defaults."""
398        await self._http.post(
399            "squid-api/v1/ai/agent/deleteCustomGuardrails",
400            {"agentId": self._agent_id},
401        )
402
403    async def update_pii(self, pii: AiPiiOptions) -> None:
404        """Update the agent's PII screening, merging with its existing settings.
405
406        With ``onDetect`` set to ``"reject"`` the agent refuses any prompt carrying
407        PII before it reaches the model, so the prompt is never answered and never
408        stored. This is the inverse of ``GuardrailsOptions.disablePii``, which asks
409        the agent's own model not to emit PII in its answer.
410
411        Args:
412            pii: An :class:`AiPiiOptions` dict with optional keys: ``onDetect``,
413                ``entities``, ``customRules``, ``classifierModel``, ``allowList``.
414
415        Example::
416
417            await agent.update_pii(
418                {
419                    "onDetect": "reject",
420                    "customRules": ["internal case numbers like CASE-12345"],
421                }
422            )
423        """
424        agent = await self.get()
425        existing = (agent or {}).get("options", {}).get("pii", {})
426        merged = {"onDetect": "off", **existing, **pii}
427        await self._http.post(
428            "squid-api/v1/ai/agent/setAgentOptionInPath",
429            {"agentId": self._agent_id, "path": "pii", "value": merged},
430        )
431
432    # --- Revisions ---
433
434    async def list_revisions(self) -> list[dict]:
435        """List all revisions of this agent.
436
437        Returns:
438            A list of ``AiAgentRevision`` dicts, each containing:
439            ``agentId``, ``revisionNumber``, ``action``, ``createdAt``,
440            ``agentSnapshot``.
441        """
442        result = await self._http.get(f"squid-api/v1/ai/agent/revisions/{self._agent_id}")
443        return result.get("revisions", []) if result else []
444
445    async def restore_revision(self, revision_number: int) -> None:
446        """Restore the agent to a previous revision.
447
448        Args:
449            revision_number: The revision number to restore.
450        """
451        await self._http.post(
452            "squid-api/v1/ai/agent/restoreRevision",
453            {"agentId": self._agent_id, "revisionNumber": revision_number},
454        )
455
456    async def delete_revision(self, revision_number: int) -> None:
457        """Delete a specific revision.
458
459        Args:
460            revision_number: The revision number to delete.
461        """
462        await self._http.post(
463            "squid-api/v1/ai/agent/deleteRevision",
464            {"agentId": self._agent_id, "revisionNumber": revision_number},
465        )

Operations on a single AI agent.

Provides methods for chatting with an agent, managing its configuration, updating guardrails, and working with revisions.

Obtained via AiClient.agent().

Example::

agent = squid.ai().agent("my-agent")
response = await agent.ask("What is the weather?", options={"temperature": 0.7})
await agent.update_instructions("Always respond in haiku format.")
AgentClient(http: squidcloud.http.HttpTransport, agent_id: str)
183    def __init__(self, http: HttpTransport, agent_id: str) -> None:
184        self._http = http
185        self._agent_id = agent_id
async def ask( self, prompt: str, options: AiChatOptions | None = None) -> str:
189    async def ask(
190        self,
191        prompt: str,
192        options: AiChatOptions | None = None,
193    ) -> str:
194        """Ask the agent a question and get a text response.
195
196        Args:
197            prompt: The user's question or instruction.
198            options: Chat options controlling model, temperature, memory,
199                functions, guardrails, and more. See :class:`AiChatOptions`.
200
201        Returns:
202            The agent's response as a string.
203
204        Raises:
205            SquidHttpError: If the agent is not found or the request fails.
206
207        Example::
208
209            response = await agent.ask(
210                "Summarize this document",
211                options={
212                    "model": "gemini-3.8-flash",
213                    "temperature": 0.3,
214                    "memoryOptions": {"memoryId": "session-1", "memoryMode": "read-write"},
215                },
216            )
217        """
218        result = await self._http.post(
219            "squid-api/v1/ai/agent/ask",
220            {"agentId": self._agent_id, "prompt": prompt, "options": options or {}},
221        )
222        return result.get("responseString", "") if result else ""

Ask the agent a question and get a text response.

Arguments:
  • prompt: The user's question or instruction.
  • options: Chat options controlling model, temperature, memory, functions, guardrails, and more. See AiChatOptions.
Returns:

The agent's response as a string.

Raises:
  • SquidHttpError: If the agent is not found or the request fails.

Example::

response = await agent.ask(
    "Summarize this document",
    options={
        "model": "gemini-3.8-flash",
        "temperature": 0.3,
        "memoryOptions": {"memoryId": "session-1", "memoryMode": "read-write"},
    },
)
async def ask_with_annotations( self, prompt: str, options: AiChatOptions | None = None) -> dict:
224    async def ask_with_annotations(
225        self,
226        prompt: str,
227        options: AiChatOptions | None = None,
228    ) -> dict:
229        """Ask the agent and get a response with annotations.
230
231        Annotations include file references, citations, and other metadata
232        the agent may attach to its response.
233
234        Args:
235            prompt: The user's question or instruction.
236            options: Chat options. See :class:`AiChatOptions`.
237
238        Returns:
239            A dict with:
240                - ``responseString`` (str): The text response.
241                - ``annotations`` (dict): Annotation metadata keyed by ID.
242
243        Example::
244
245            result = await agent.ask_with_annotations("Find relevant docs")
246            print(result["responseString"])
247            for ann_id, ann in result.get("annotations", {}).items():
248                print(f"  Annotation: {ann}")
249        """
250        return await self._http.post(
251            "squid-api/v1/ai/agent/askWithAnnotations",
252            {"agentId": self._agent_id, "prompt": prompt, "options": options or {}},
253        )

Ask the agent and get a response with annotations.

Annotations include file references, citations, and other metadata the agent may attach to its response.

Arguments:
  • prompt: The user's question or instruction.
  • options: Chat options. See AiChatOptions.
Returns:

A dict with: - responseString (str): The text response. - annotations (dict): Annotation metadata keyed by ID.

Example::

result = await agent.ask_with_annotations("Find relevant docs")
print(result["responseString"])
for ann_id, ann in result.get("annotations", {}).items():
    print(f"  Annotation: {ann}")
async def get(self) -> dict | None:
257    async def get(self) -> dict | None:
258        """Get the agent's configuration.
259
260        Returns:
261            An ``AiAgent`` dict with keys: ``id``, ``createdAt``, ``updatedAt``,
262            ``description``, ``isPublic``, ``auditLog``, ``auditLogFullContext``,
263            ``options``, ``apiKey``.
264            Returns ``None`` if the agent does not exist.
265        """
266        return await self._http.get(f"squid-api/v1/ai/agent/get/{self._agent_id}")

Get the agent's configuration.

Returns:

An AiAgent dict with keys: id, createdAt, updatedAt, description, isPublic, auditLog, auditLogFullContext, options, apiKey. Returns None if the agent does not exist.

async def upsert( self, *, description: str | None = None, is_public: bool | None = None, audit_log: bool | None = None, audit_log_full_context: bool | None = None, api_key: str | None = None, mcp_server: AiAgentMcpServerConfig | None = None, options: AiChatOptions | None = None) -> None:
268    async def upsert(
269        self,
270        *,
271        description: str | None = None,
272        is_public: bool | None = None,
273        audit_log: bool | None = None,
274        audit_log_full_context: bool | None = None,
275        api_key: str | None = None,
276        mcp_server: AiAgentMcpServerConfig | None = None,
277        options: AiChatOptions | None = None,
278    ) -> None:
279        """Create or update the agent.
280
281        If the agent does not exist, it is created. If it exists, the provided
282        fields are updated (fields set to ``None`` are left unchanged).
283
284        Args:
285            description: A description of the agent's purpose or capabilities.
286            is_public: Whether the agent is publicly accessible (default ``False``).
287            audit_log: Enable audit logging for the agent's activities.
288            audit_log_full_context: Record the full agent context (system
289                instructions and retrieved knowledge-base content) in the audit
290                log; requires ``audit_log``.
291            api_key: Optional API key used specifically for this agent.
292            mcp_server: Configuration for exposing the agent as an MCP server
293                at ``/mcp/<agentId>``. See :class:`AiAgentMcpServerConfig`.
294            options: Default chat options applied to every request unless
295                overridden per-call. See :class:`AiChatOptions`.
296
297        Example::
298
299            await agent.upsert(
300                description="Customer support agent",
301                options={"model": "gemini-3.8-flash", "temperature": 0.5},
302            )
303        """
304        body: dict[str, Any] = {"id": self._agent_id}
305        if description is not None:
306            body["description"] = description
307        if is_public is not None:
308            body["isPublic"] = is_public
309        if audit_log is not None:
310            body["auditLog"] = audit_log
311        if audit_log_full_context is not None:
312            body["auditLogFullContext"] = audit_log_full_context
313        if api_key is not None:
314            body["apiKey"] = api_key
315        if mcp_server is not None:
316            body["mcpServer"] = mcp_server
317        if options is not None:
318            body["options"] = options
319        await self._http.post("squid-api/v1/ai/agent/upsert", body)

Create or update the agent.

If the agent does not exist, it is created. If it exists, the provided fields are updated (fields set to None are left unchanged).

Arguments:
  • description: A description of the agent's purpose or capabilities.
  • is_public: Whether the agent is publicly accessible (default False).
  • audit_log: Enable audit logging for the agent's activities.
  • audit_log_full_context: Record the full agent context (system instructions and retrieved knowledge-base content) in the audit log; requires audit_log.
  • api_key: Optional API key used specifically for this agent.
  • mcp_server: Configuration for exposing the agent as an MCP server at /mcp/<agentId>. See AiAgentMcpServerConfig.
  • options: Default chat options applied to every request unless overridden per-call. See AiChatOptions.

Example::

await agent.upsert(
    description="Customer support agent",
    options={"model": "gemini-3.8-flash", "temperature": 0.5},
)
async def delete(self) -> None:
321    async def delete(self) -> None:
322        """Delete the agent permanently."""
323        await self._http.post("squid-api/v1/ai/agent/delete", {"agentId": self._agent_id})

Delete the agent permanently.

async def update_instructions(self, instructions: str) -> None:
325    async def update_instructions(self, instructions: str) -> None:
326        """Update the agent's system instructions.
327
328        Args:
329            instructions: The new system prompt / instructions text.
330        """
331        await self._http.post(
332            "squid-api/v1/ai/agent/updateInstructions",
333            {"agentId": self._agent_id, "instructions": instructions},
334        )

Update the agent's system instructions.

Arguments:
  • instructions: The new system prompt / instructions text.
async def update_model(self, model: str | IntegrationModelSpec) -> None:
336    async def update_model(self, model: AiChatModelSelection) -> None:
337        """Update the agent's default LLM model.
338
339        Args:
340            model: A model name string (e.g., ``'gemini-3.8-flash'``) or an
341                ``IntegrationModelSpec`` dict (``{'integrationId': str, 'model': str}``).
342        """
343        await self._http.post(
344            "squid-api/v1/ai/agent/updateModel",
345            {"agentId": self._agent_id, "model": model},
346        )

Update the agent's default LLM model.

Arguments:
  • model: A model name string (e.g., 'gemini-3.8-flash') or an IntegrationModelSpec dict ({'integrationId': str, 'model': str}).
async def update_connected_agents( self, connected_agents: list[AiConnectedAgentMetadata]) -> None:
348    async def update_connected_agents(
349        self, connected_agents: list[AiConnectedAgentMetadata]
350    ) -> None:
351        """Update the list of connected agents.
352
353        Connected agents can be called by this agent during conversations.
354
355        Args:
356            connected_agents: List of ``{'agentId': str, 'description': str}`` dicts.
357        """
358        await self._http.post(
359            "squid-api/v1/ai/agent/updateConnectedAgents",
360            {"agentId": self._agent_id, "connectedAgents": connected_agents},
361        )

Update the list of connected agents.

Connected agents can be called by this agent during conversations.

Arguments:
  • connected_agents: List of {'agentId': str, 'description': str} dicts.
async def update_guardrails(self, guardrails: GuardrailsOptions) -> None:
363    async def update_guardrails(self, guardrails: GuardrailsOptions) -> None:
364        """Update the agent's guardrail settings.
365
366        Args:
367            guardrails: A :class:`GuardrailsOptions` dict with optional keys:
368                ``custom``, ``disablePii``, ``professionalTone``,
369                ``offTopicAnswers``, ``disableProfanity``.
370
371        Example::
372
373            await agent.update_guardrails(
374                {
375                    "professionalTone": True,
376                    "disableProfanity": True,
377                }
378            )
379        """
380        await self._http.post(
381            "squid-api/v1/ai/agent/updateGuardrails",
382            {"agentId": self._agent_id, "guardrails": guardrails},
383        )

Update the agent's guardrail settings.

Arguments:
  • guardrails: A GuardrailsOptions dict with optional keys: custom, disablePii, professionalTone, offTopicAnswers, disableProfanity.

Example::

await agent.update_guardrails(
    {
        "professionalTone": True,
        "disableProfanity": True,
    }
)
async def update_custom_guardrails(self, custom_guardrail: str) -> None:
385    async def update_custom_guardrails(self, custom_guardrail: str) -> None:
386        """Update the custom guardrail instruction text.
387
388        Args:
389            custom_guardrail: Free-form guardrail instruction string.
390        """
391        await self._http.post(
392            "squid-api/v1/ai/agent/updateCustomGuardrails",
393            {"agentId": self._agent_id, "customGuardrail": custom_guardrail},
394        )

Update the custom guardrail instruction text.

Arguments:
  • custom_guardrail: Free-form guardrail instruction string.
async def delete_custom_guardrails(self) -> None:
396    async def delete_custom_guardrails(self) -> None:
397        """Delete the custom guardrail, reverting to defaults."""
398        await self._http.post(
399            "squid-api/v1/ai/agent/deleteCustomGuardrails",
400            {"agentId": self._agent_id},
401        )

Delete the custom guardrail, reverting to defaults.

async def update_pii(self, pii: AiPiiOptions) -> None:
403    async def update_pii(self, pii: AiPiiOptions) -> None:
404        """Update the agent's PII screening, merging with its existing settings.
405
406        With ``onDetect`` set to ``"reject"`` the agent refuses any prompt carrying
407        PII before it reaches the model, so the prompt is never answered and never
408        stored. This is the inverse of ``GuardrailsOptions.disablePii``, which asks
409        the agent's own model not to emit PII in its answer.
410
411        Args:
412            pii: An :class:`AiPiiOptions` dict with optional keys: ``onDetect``,
413                ``entities``, ``customRules``, ``classifierModel``, ``allowList``.
414
415        Example::
416
417            await agent.update_pii(
418                {
419                    "onDetect": "reject",
420                    "customRules": ["internal case numbers like CASE-12345"],
421                }
422            )
423        """
424        agent = await self.get()
425        existing = (agent or {}).get("options", {}).get("pii", {})
426        merged = {"onDetect": "off", **existing, **pii}
427        await self._http.post(
428            "squid-api/v1/ai/agent/setAgentOptionInPath",
429            {"agentId": self._agent_id, "path": "pii", "value": merged},
430        )

Update the agent's PII screening, merging with its existing settings.

With onDetect set to "reject" the agent refuses any prompt carrying PII before it reaches the model, so the prompt is never answered and never stored. This is the inverse of GuardrailsOptions.disablePii, which asks the agent's own model not to emit PII in its answer.

Arguments:
  • pii: An AiPiiOptions dict with optional keys: onDetect, entities, customRules, classifierModel, allowList.

Example::

await agent.update_pii(
    {
        "onDetect": "reject",
        "customRules": ["internal case numbers like CASE-12345"],
    }
)
async def list_revisions(self) -> list[dict]:
434    async def list_revisions(self) -> list[dict]:
435        """List all revisions of this agent.
436
437        Returns:
438            A list of ``AiAgentRevision`` dicts, each containing:
439            ``agentId``, ``revisionNumber``, ``action``, ``createdAt``,
440            ``agentSnapshot``.
441        """
442        result = await self._http.get(f"squid-api/v1/ai/agent/revisions/{self._agent_id}")
443        return result.get("revisions", []) if result else []

List all revisions of this agent.

Returns:

A list of AiAgentRevision dicts, each containing: agentId, revisionNumber, action, createdAt, agentSnapshot.

async def restore_revision(self, revision_number: int) -> None:
445    async def restore_revision(self, revision_number: int) -> None:
446        """Restore the agent to a previous revision.
447
448        Args:
449            revision_number: The revision number to restore.
450        """
451        await self._http.post(
452            "squid-api/v1/ai/agent/restoreRevision",
453            {"agentId": self._agent_id, "revisionNumber": revision_number},
454        )

Restore the agent to a previous revision.

Arguments:
  • revision_number: The revision number to restore.
async def delete_revision(self, revision_number: int) -> None:
456    async def delete_revision(self, revision_number: int) -> None:
457        """Delete a specific revision.
458
459        Args:
460            revision_number: The revision number to delete.
461        """
462        await self._http.post(
463            "squid-api/v1/ai/agent/deleteRevision",
464            {"agentId": self._agent_id, "revisionNumber": revision_number},
465        )

Delete a specific revision.

Arguments:
  • revision_number: The revision number to delete.
class AiAgent(typing.TypedDict):
426class AiAgent(TypedDict, total=False):
427    """A definition of an AI agent with its properties and default chat options.
428
429    Returned by :meth:`AiClient.list_agents`.
430    """
431
432    id: str
433    """The unique identifier of the AI agent. Required."""
434    createdAt: str
435    """ISO 8601 timestamp of when the agent was created. Required."""
436    updatedAt: str
437    """ISO 8601 timestamp of when the agent was last updated. Required."""
438    description: str
439    """An optional description of the agent's purpose or capabilities."""
440    isPublic: bool
441    """Whether the agent is publicly accessible; defaults to False."""
442    auditLog: bool
443    """Whether audit logging is enabled for the agent's activities; defaults to False."""
444    auditLogFullContext: bool
445    """Whether the full agent context (system instructions and retrieved knowledge-base content) is recorded in the audit log; requires auditLog; defaults to False."""
446    options: AiChatOptions
447    """The agent's default chat options, overridable by the user during use. Required."""
448    apiKey: str
449    """Optional API key used specifically for operations on this agent."""
450    mcpServer: AiAgentMcpServerConfig
451    """Optional configuration for exposing the agent as an MCP server."""

A definition of an AI agent with its properties and default chat options.

Returned by AiClient.list_agents().

id: str

The unique identifier of the AI agent. Required.

createdAt: str

ISO 8601 timestamp of when the agent was created. Required.

updatedAt: str

ISO 8601 timestamp of when the agent was last updated. Required.

description: str

An optional description of the agent's purpose or capabilities.

isPublic: bool

Whether the agent is publicly accessible; defaults to False.

auditLog: bool

Whether audit logging is enabled for the agent's activities; defaults to False.

auditLogFullContext: bool

Whether the full agent context (system instructions and retrieved knowledge-base content) is recorded in the audit log; requires auditLog; defaults to False.

options: AiChatOptions

The agent's default chat options, overridable by the user during use. Required.

apiKey: str

Optional API key used specifically for operations on this agent.

Optional configuration for exposing the agent as an MCP server.

class AiAgentExecutionPlanOptions(typing.TypedDict):
166class AiAgentExecutionPlanOptions(TypedDict, total=False):
167    """Options for AI agent execution plan."""
168
169    enabled: bool

Options for AI agent execution plan.

enabled: bool
class AiAgentMcpServerConfig(typing.TypedDict):
392class AiAgentMcpServerConfig(TypedDict, total=False):
393    """Configuration for exposing an AI agent as an MCP server at ``/mcp/<agentId>``."""
394
395    enabled: bool
396    """Whether the agent is exposed as an MCP server. Required."""
397    description: str
398    """Description of the MCP server, served as the ``instructions`` field of the MCP initialize result."""
399    toolDescription: str
400    """Description of the MCP ``ask`` tool, exposed in the tools manifest."""
401    oauthIntegrationId: str
402    """ID of an auth integration used to OAuth-protect the MCP server."""
403    requireApiKey: bool
404    """Require the agent's API key as a bearer token; combinable with OAuth (either is accepted)."""

Configuration for exposing an AI agent as an MCP server at /mcp/<agentId>.

enabled: bool

Whether the agent is exposed as an MCP server. Required.

description: str

Description of the MCP server, served as the instructions field of the MCP initialize result.

toolDescription: str

Description of the MCP ask tool, exposed in the tools manifest.

oauthIntegrationId: str

ID of an auth integration used to OAuth-protect the MCP server.

requireApiKey: bool

Require the agent's API key as a bearer token; combinable with OAuth (either is accepted).

class AiAgentMemoryOptions(typing.TypedDict):
92class AiAgentMemoryOptions(TypedDict, total=False):
93    """Memory/session configuration for agent chat."""
94
95    memoryId: str
96    """Unique memory ID. Reuse to continue a conversation."""
97    memoryMode: AiMemoryMode
98    """How memory is used: 'read-write', 'read-only', 'write-only', 'disabled'."""

Memory/session configuration for agent chat.

memoryId: str

Unique memory ID. Reuse to continue a conversation.

memoryMode: Literal['read-write', 'read-only', 'write-only', 'disabled']

How memory is used: 'read-write', 'read-only', 'write-only', 'disabled'.

AiAgentResponseFormat = typing.Literal['text', 'json_object'] | AiStructuredOutputFormat
class AiAudioCreateSpeechOptions(typing.TypedDict):
206class AiAudioCreateSpeechOptions(TypedDict, total=False):
207    """Options for text-to-speech."""
208
209    modelName: str
210    """'tts-1' or 'tts-1-hd'."""
211    voice: str
212    """'alloy'|'ash'|'ballad'|'coral'|'echo'|'fable'|'onyx'|'nova'|'sage'|'shimmer'|'verse'."""
213    responseFormat: str
214    """Audio output format."""
215    instructions: str
216    """Extra instructions for speech generation."""
217    speed: float
218    """Speech speed."""

Options for text-to-speech.

modelName: str

'tts-1' or 'tts-1-hd'.

voice: str

'alloy'|'ash'|'ballad'|'coral'|'echo'|'fable'|'onyx'|'nova'|'sage'|'shimmer'|'verse'.

responseFormat: str

Audio output format.

instructions: str

Extra instructions for speech generation.

speed: float

Speech speed.

AiChatModelSelection = str | IntegrationModelSpec
class AiChatOptions(typing.TypedDict):
221class AiChatOptions(TypedDict, total=False):
222    """Full chat options for AI agent ask/chat operations.
223
224    Mirrors BaseAiChatOptions from the TypeScript SDK.
225    All fields are optional.
226    """
227
228    model: AiChatModelSelection
229    """LLM model to use. String name or IntegrationModelSpec."""
230    maxTokens: int
231    """Max input tokens for the AI model."""
232    maxOutputTokens: int
233    """Max output tokens from the AI model."""
234    temperature: float
235    """Sampling temperature (default 0.5)."""
236    instructions: str
237    """Extra instructions to include with the prompt."""
238    functions: list[str]
239    """AI function IDs to expose to the agent."""
240    memoryOptions: AiAgentMemoryOptions
241    """Memory/session configuration."""
242    responseFormat: AiAgentResponseFormat
243    """Response format: 'text', 'json_object', or structured output."""
244    includeReference: bool
245    """Include source references from context."""
246    citation: bool
247    """Cite the sources behind the agent's statements."""
248    allowSourceDownloads: bool
249    """Allow citation links to the original files. Read from the stored agent only; ignored when passed with a request."""
250    smoothTyping: bool
251    """Smooth typing effect for UI display (default true)."""
252    disableContext: bool
253    """Disable the whole context for this request."""
254    enablePromptRewriteForRag: bool
255    """Rewrite prompt for RAG (default false)."""
256    agentContext: dict[str, Any]
257    """Global context passed to agent and all AI functions."""
258    connectedAgents: list[AiConnectedAgentMetadata]
259    """Connected agents that can be called."""
260    connectedIntegrations: list[AiConnectedIntegrationMetadata]
261    """Connected integrations."""
262    connectedKnowledgeBases: list[AiConnectedKnowledgeBaseMetadata]
263    """Connected knowledge bases."""
264    guardrails: GuardrailsOptions
265    """Guardrail options."""
266    pii: AiPiiOptions
267    """PII screening. Read from the stored agent only; ignored when passed with a request."""
268    contextMetadataFilterForKnowledgeBase: dict[str, Any]
269    """Metadata filters per knowledge base ID."""
270    voiceOptions: AiAudioCreateSpeechOptions
271    """Options for voice response."""
272    quotas: AiChatPromptQuotas
273    """Budget for nested AI calls."""
274    executionPlanOptions: AiAgentExecutionPlanOptions
275    """Execution plan options."""
276    fileUrls: list[AiFileUrl]
277    """File URLs to include in context."""
278    fileIds: list[str]
279    """File IDs from AI provider's Files API."""
280    reasoningEffort: AiReasoningEffort
281    """Reasoning effort level."""
282    verbosity: AiVerbosityLevel
283    """Response verbosity level."""
284    useCodeInterpreter: Literal["none", "llm"]
285    """Enable LLM's built-in code interpreter."""
286    rerankProvider: AiRerankProvider
287    """Reranker provider for context (default 'cohere')."""
288    rerankScoreThreshold: float
289    """Minimum reranker relevance score a chunk must reach to stay once at least 10 chunks are
290    already kept (default 0.2). Score ranges differ per reranker."""
291    includeMetadata: bool
292    """Include metadata in context (deprecated)."""
293    timeoutMs: int
294    """Request timeout in milliseconds (default 240000)."""

Full chat options for AI agent ask/chat operations.

Mirrors BaseAiChatOptions from the TypeScript SDK. All fields are optional.

model: str | IntegrationModelSpec

LLM model to use. String name or IntegrationModelSpec.

maxTokens: int

Max input tokens for the AI model.

maxOutputTokens: int

Max output tokens from the AI model.

temperature: float

Sampling temperature (default 0.5).

instructions: str

Extra instructions to include with the prompt.

functions: list[str]

AI function IDs to expose to the agent.

memoryOptions: AiAgentMemoryOptions

Memory/session configuration.

responseFormat: Literal['text', 'json_object'] | AiStructuredOutputFormat

Response format: 'text', 'json_object', or structured output.

includeReference: bool

Include source references from context.

citation: bool

Cite the sources behind the agent's statements.

allowSourceDownloads: bool

Allow citation links to the original files. Read from the stored agent only; ignored when passed with a request.

smoothTyping: bool

Smooth typing effect for UI display (default true).

disableContext: bool

Disable the whole context for this request.

enablePromptRewriteForRag: bool

Rewrite prompt for RAG (default false).

agentContext: dict[str, typing.Any]

Global context passed to agent and all AI functions.

connectedAgents: list[AiConnectedAgentMetadata]

Connected agents that can be called.

connectedIntegrations: list[AiConnectedIntegrationMetadata]

Connected integrations.

connectedKnowledgeBases: list[AiConnectedKnowledgeBaseMetadata]

Connected knowledge bases.

guardrails: GuardrailsOptions

Guardrail options.

PII screening. Read from the stored agent only; ignored when passed with a request.

contextMetadataFilterForKnowledgeBase: dict[str, typing.Any]

Metadata filters per knowledge base ID.

Options for voice response.

Budget for nested AI calls.

executionPlanOptions: AiAgentExecutionPlanOptions

Execution plan options.

fileUrls: list[AiFileUrl]

File URLs to include in context.

fileIds: list[str]

File IDs from AI provider's Files API.

reasoningEffort: Literal['minimal', 'low', 'medium', 'high', 'xhigh', 'max'] | str

Reasoning effort level.

verbosity: Literal['low', 'medium', 'high']

Response verbosity level.

useCodeInterpreter: Literal['none', 'llm']

Enable LLM's built-in code interpreter.

rerankProvider: Literal['cohere', 'voyage', 'none']

Reranker provider for context (default 'cohere').

rerankScoreThreshold: float

Minimum reranker relevance score a chunk must reach to stay once at least 10 chunks are already kept (default 0.2). Score ranges differ per reranker.

includeMetadata: bool

Include metadata in context (deprecated).

timeoutMs: int

Request timeout in milliseconds (default 240000).

class AiChatPromptQuotas(typing.TypedDict):
160class AiChatPromptQuotas(TypedDict, total=False):
161    """Budget for nested/recursive AI chat calls."""
162
163    maxNestedCalls: int

Budget for nested/recursive AI chat calls.

maxNestedCalls: int
class AiClient:
 38class AiClient:
 39    """Entry point for AI operations: agents, knowledge base, audio, image.
 40
 41    Obtained via :meth:`Squid.ai()`.
 42
 43    Example::
 44
 45        ai = squid.ai()
 46        agent = ai.agent("my-agent")
 47        kb = ai.knowledge_base("my-kb")
 48        image_url = await ai.image().generate("a cat in space")
 49    """
 50
 51    def __init__(self, http: HttpTransport) -> None:
 52        self._http = http
 53
 54    def agent(self, agent_id: str) -> AgentClient:
 55        """Get a client for a specific AI agent.
 56
 57        Args:
 58            agent_id: The unique agent identifier.
 59
 60        Returns:
 61            An :class:`AgentClient` bound to the given agent ID.
 62        """
 63        return AgentClient(self._http, agent_id)
 64
 65    def knowledge_base(self, knowledge_base_id: str) -> KnowledgeBaseClient:
 66        """Get a client for a specific knowledge base.
 67
 68        Args:
 69            knowledge_base_id: The unique knowledge base identifier.
 70
 71        Returns:
 72            A :class:`KnowledgeBaseClient` bound to the given knowledge base ID.
 73        """
 74        return KnowledgeBaseClient(self._http, knowledge_base_id)
 75
 76    def image(self) -> ImageClient:
 77        """Get a client for image generation and processing.
 78
 79        Returns:
 80            An :class:`ImageClient` instance.
 81        """
 82        return ImageClient(self._http)
 83
 84    def audio(self) -> AudioClient:
 85        """Get a client for audio transcription and speech synthesis.
 86
 87        Returns:
 88            An :class:`AudioClient` instance.
 89        """
 90        return AudioClient(self._http)
 91
 92    async def list_agents(self) -> list[AiAgent]:
 93        """List all AI agents defined for the application.
 94
 95        Returns:
 96            A list of ``AiAgent`` dicts. Empty list if no agents are defined.
 97        """
 98        result = await self._http.get("squid-api/v1/ai/agent/listAgents")
 99        return result.get("agents", []) if result else []
100
101    async def list_knowledge_bases(self) -> list[AiKnowledgeBase]:
102        """List all AI knowledge bases defined for the application.
103
104        Returns:
105            A list of ``AiKnowledgeBase`` dicts. Empty list if no knowledge
106            bases are defined.
107        """
108        result = await self._http.get("squid-api/v1/ai/knowledge-base/listKnowledgeBases")
109        return result.get("knowledgeBases", []) if result else []
110
111    async def list_chat_models(self, include_deprecated: bool = False) -> list[ModelIdSpec]:
112        """List all AI chat models available to the application.
113
114        Includes both Squid-provided vendor models and any custom integration
115        models configured for the app.
116
117        Args:
118            include_deprecated: When True, deprecated vendor models are
119                included and marked with ``replacedBy``. Defaults to False.
120
121        Returns:
122            A list of ``ModelIdSpec`` dicts, each with ``modelId``, an optional
123            ``integrationId``, and a human-readable ``displayName``. Vendor
124            models also carry a ``description``, and deprecated vendor models a
125            ``replacedBy`` with the active model their calls are routed to.
126        """
127        params = {"includeDeprecated": "true"} if include_deprecated else None
128        result = await self._http.get("squid-api/v1/ai/settings/listChatModels", params=params)
129        return result.get("models", []) if result else []
130
131    async def list_embedding_models(
132        self, include_deprecated: bool = False
133    ) -> list[EmbeddingModelIdSpec]:
134        """List the embedding models a new knowledge base can be created on.
135
136        Includes Squid-provided vendor models and the app's custom integration
137        models. Exactly one entry carries ``isDefault``.
138
139        Args:
140            include_deprecated: When True, deprecated vendor models are
141                included and marked with ``replacedBy``. Defaults to False.
142
143        Returns:
144            A list of ``EmbeddingModelIdSpec`` dicts, each with ``modelId``,
145            ``displayName`` and ``source``, plus ``integrationId`` and
146            ``dimensions`` for integration models.
147        """
148        params = {"includeDeprecated": "true"} if include_deprecated else None
149        result = await self._http.get("squid-api/v1/ai/settings/listEmbeddingModels", params=params)
150        return result.get("models", []) if result else []
151
152    async def list_functions(self) -> list[AiFunctionMetadata]:
153        """List all AI functions registered for the application's deployed bundle.
154
155        Returns:
156            A list of ``AiFunctionMetadata`` dicts. Empty list if no functions
157            are registered.
158        """
159        result = await self._http.get("squid-api/v1/ai/function/listFunctions")
160        return result.get("functions", []) if result else []

Entry point for AI operations: agents, knowledge base, audio, image.

Obtained via Squid.ai()().

Example::

ai = squid.ai()
agent = ai.agent("my-agent")
kb = ai.knowledge_base("my-kb")
image_url = await ai.image().generate("a cat in space")
AiClient(http: squidcloud.http.HttpTransport)
51    def __init__(self, http: HttpTransport) -> None:
52        self._http = http
def agent(self, agent_id: str) -> AgentClient:
54    def agent(self, agent_id: str) -> AgentClient:
55        """Get a client for a specific AI agent.
56
57        Args:
58            agent_id: The unique agent identifier.
59
60        Returns:
61            An :class:`AgentClient` bound to the given agent ID.
62        """
63        return AgentClient(self._http, agent_id)

Get a client for a specific AI agent.

Arguments:
  • agent_id: The unique agent identifier.
Returns:

An AgentClient bound to the given agent ID.

def knowledge_base(self, knowledge_base_id: str) -> KnowledgeBaseClient:
65    def knowledge_base(self, knowledge_base_id: str) -> KnowledgeBaseClient:
66        """Get a client for a specific knowledge base.
67
68        Args:
69            knowledge_base_id: The unique knowledge base identifier.
70
71        Returns:
72            A :class:`KnowledgeBaseClient` bound to the given knowledge base ID.
73        """
74        return KnowledgeBaseClient(self._http, knowledge_base_id)

Get a client for a specific knowledge base.

Arguments:
  • knowledge_base_id: The unique knowledge base identifier.
Returns:

A KnowledgeBaseClient bound to the given knowledge base ID.

def image(self) -> ImageClient:
76    def image(self) -> ImageClient:
77        """Get a client for image generation and processing.
78
79        Returns:
80            An :class:`ImageClient` instance.
81        """
82        return ImageClient(self._http)

Get a client for image generation and processing.

Returns:

An ImageClient instance.

def audio(self) -> AudioClient:
84    def audio(self) -> AudioClient:
85        """Get a client for audio transcription and speech synthesis.
86
87        Returns:
88            An :class:`AudioClient` instance.
89        """
90        return AudioClient(self._http)

Get a client for audio transcription and speech synthesis.

Returns:

An AudioClient instance.

async def list_agents(self) -> list[AiAgent]:
92    async def list_agents(self) -> list[AiAgent]:
93        """List all AI agents defined for the application.
94
95        Returns:
96            A list of ``AiAgent`` dicts. Empty list if no agents are defined.
97        """
98        result = await self._http.get("squid-api/v1/ai/agent/listAgents")
99        return result.get("agents", []) if result else []

List all AI agents defined for the application.

Returns:

A list of AiAgent dicts. Empty list if no agents are defined.

async def list_knowledge_bases(self) -> list[AiKnowledgeBase]:
101    async def list_knowledge_bases(self) -> list[AiKnowledgeBase]:
102        """List all AI knowledge bases defined for the application.
103
104        Returns:
105            A list of ``AiKnowledgeBase`` dicts. Empty list if no knowledge
106            bases are defined.
107        """
108        result = await self._http.get("squid-api/v1/ai/knowledge-base/listKnowledgeBases")
109        return result.get("knowledgeBases", []) if result else []

List all AI knowledge bases defined for the application.

Returns:

A list of AiKnowledgeBase dicts. Empty list if no knowledge bases are defined.

async def list_chat_models( self, include_deprecated: bool = False) -> list[ModelIdSpec]:
111    async def list_chat_models(self, include_deprecated: bool = False) -> list[ModelIdSpec]:
112        """List all AI chat models available to the application.
113
114        Includes both Squid-provided vendor models and any custom integration
115        models configured for the app.
116
117        Args:
118            include_deprecated: When True, deprecated vendor models are
119                included and marked with ``replacedBy``. Defaults to False.
120
121        Returns:
122            A list of ``ModelIdSpec`` dicts, each with ``modelId``, an optional
123            ``integrationId``, and a human-readable ``displayName``. Vendor
124            models also carry a ``description``, and deprecated vendor models a
125            ``replacedBy`` with the active model their calls are routed to.
126        """
127        params = {"includeDeprecated": "true"} if include_deprecated else None
128        result = await self._http.get("squid-api/v1/ai/settings/listChatModels", params=params)
129        return result.get("models", []) if result else []

List all AI chat models available to the application.

Includes both Squid-provided vendor models and any custom integration models configured for the app.

Arguments:
  • include_deprecated: When True, deprecated vendor models are included and marked with replacedBy. Defaults to False.
Returns:

A list of ModelIdSpec dicts, each with modelId, an optional integrationId, and a human-readable displayName. Vendor models also carry a description, and deprecated vendor models a replacedBy with the active model their calls are routed to.

async def list_embedding_models( self, include_deprecated: bool = False) -> list[EmbeddingModelIdSpec]:
131    async def list_embedding_models(
132        self, include_deprecated: bool = False
133    ) -> list[EmbeddingModelIdSpec]:
134        """List the embedding models a new knowledge base can be created on.
135
136        Includes Squid-provided vendor models and the app's custom integration
137        models. Exactly one entry carries ``isDefault``.
138
139        Args:
140            include_deprecated: When True, deprecated vendor models are
141                included and marked with ``replacedBy``. Defaults to False.
142
143        Returns:
144            A list of ``EmbeddingModelIdSpec`` dicts, each with ``modelId``,
145            ``displayName`` and ``source``, plus ``integrationId`` and
146            ``dimensions`` for integration models.
147        """
148        params = {"includeDeprecated": "true"} if include_deprecated else None
149        result = await self._http.get("squid-api/v1/ai/settings/listEmbeddingModels", params=params)
150        return result.get("models", []) if result else []

List the embedding models a new knowledge base can be created on.

Includes Squid-provided vendor models and the app's custom integration models. Exactly one entry carries isDefault.

Arguments:
  • include_deprecated: When True, deprecated vendor models are included and marked with replacedBy. Defaults to False.
Returns:

A list of EmbeddingModelIdSpec dicts, each with modelId, displayName and source, plus integrationId and dimensions for integration models.

async def list_functions(self) -> list[AiFunctionMetadata]:
152    async def list_functions(self) -> list[AiFunctionMetadata]:
153        """List all AI functions registered for the application's deployed bundle.
154
155        Returns:
156            A list of ``AiFunctionMetadata`` dicts. Empty list if no functions
157            are registered.
158        """
159        result = await self._http.get("squid-api/v1/ai/function/listFunctions")
160        return result.get("functions", []) if result else []

List all AI functions registered for the application's deployed bundle.

Returns:

A list of AiFunctionMetadata dicts. Empty list if no functions are registered.

class AiConnectedAgentMetadata(typing.TypedDict):
101class AiConnectedAgentMetadata(TypedDict):
102    """Metadata for a connected agent."""
103
104    agentId: str
105    description: str

Metadata for a connected agent.

agentId: str
description: str
class AiConnectedIntegrationMetadata(typing.TypedDict):
108class AiConnectedIntegrationMetadata(TypedDict, total=False):
109    """Metadata for a connected integration.
110
111    ``integrationId`` and ``integrationType`` are required by the platform API;
112    the remaining fields are optional.
113    """
114
115    integrationId: str
116    """The ID of the connected integration. Required."""
117    integrationType: str
118    """The integration type, e.g. 'hubspot', 'slack', 'api'. Required."""
119    description: str
120    """Optional description used as the AI function description for the parent agent."""
121    instructions: str
122    """Optional instructions for the connected integration agent, overriding the default."""
123    functionsToUse: list[str]
124    """AI function IDs the agent may use. Omit for all functions; [] for none."""
125    options: dict[str, Any]
126    """Additional integration options interpreted by Squid Core or connector AI functions."""
127    connectedAsMcp: bool
128    """Treat this integration as an MCP server (API integrations with exposeAsMcpServer)."""

Metadata for a connected integration.

integrationId and integrationType are required by the platform API; the remaining fields are optional.

integrationId: str

The ID of the connected integration. Required.

integrationType: str

The integration type, e.g. 'hubspot', 'slack', 'api'. Required.

description: str

Optional description used as the AI function description for the parent agent.

instructions: str

Optional instructions for the connected integration agent, overriding the default.

functionsToUse: list[str]

AI function IDs the agent may use. Omit for all functions; [] for none.

options: dict[str, typing.Any]

Additional integration options interpreted by Squid Core or connector AI functions.

connectedAsMcp: bool

Treat this integration as an MCP server (API integrations with exposeAsMcpServer).

class AiConnectedKnowledgeBaseMetadata(typing.TypedDict):
131class AiConnectedKnowledgeBaseMetadata(TypedDict, total=False):
132    """Metadata for a connected knowledge base."""
133
134    knowledgeBaseId: str
135    description: str
136    includeMetadata: bool

Metadata for a connected knowledge base.

knowledgeBaseId: str
description: str
includeMetadata: bool
class AiContextFileOptions(typing.TypedDict):
609class AiContextFileOptions(TypedDict, total=False):
610    """Options for file context processing."""
611
612    chunkOverlap: int
613    ragType: str

Options for file context processing.

chunkOverlap: int
ragType: str
class AiContextTextOptions(typing.TypedDict):
589class AiContextTextOptions(TypedDict, total=False):
590    """Options for text context processing."""
591
592    chunkOverlap: int
593    """Amount of chunk overlap in characters."""
594    ragType: str
595    """The type of RAG to use."""

Options for text context processing.

chunkOverlap: int

Amount of chunk overlap in characters.

ragType: str

The type of RAG to use.

AiEmbeddingsModelSelection = str | IntegrationEmbeddingModelSpec
class AiFileUrl(typing.TypedDict):
139class AiFileUrl(TypedDict, total=False):
140    """File URL to include in chat context."""
141
142    id: str
143    type: str
144    purpose: str
145    url: str
146    description: str
147    fileName: str

File URL to include in chat context.

id: str
type: str
purpose: str
url: str
description: str
fileName: str
class AiFunctionAttributes(typing.TypedDict):
730class AiFunctionAttributes(TypedDict, total=False):
731    """Additional optional readonly metadata for an AI function."""
732
733    integrationType: list[str]
734    """Types of integration this function is used for. Functions with a defined
735    'integrationType' require 'integrationId' to be passed as part of the function context."""

Additional optional readonly metadata for an AI function.

integrationType: list[str]

Types of integration this function is used for. Functions with a defined 'integrationType' require 'integrationId' to be passed as part of the function context.

class AiFunctionMetadata(typing.TypedDict):
738class AiFunctionMetadata(TypedDict, total=False):
739    """Metadata describing an AI function available in the application.
740
741    Returned by :meth:`AiClient.list_functions`.
742    """
743
744    serviceFunction: str
745    """The fully qualified name of the function ('ServiceName:functionName'). Required."""
746    description: str
747    """Description of what the function does."""
748    promptId: str
749    """Opaque ID of a registered prompt that supplies this function's description;
750    resolved server-side."""
751    params: list[AiFunctionParam]
752    """Parameters that the function accepts. Required."""
753    attributes: AiFunctionAttributes
754    """Additional attributes for the function."""
755    categories: list[str]
756    """Categories this function belongs to."""
757    internal: bool
758    """Whether this function is internal and not meant for direct use."""

Metadata describing an AI function available in the application.

Returned by AiClient.list_functions().

serviceFunction: str

The fully qualified name of the function ('ServiceName:functionName'). Required.

description: str

Description of what the function does.

promptId: str

Opaque ID of a registered prompt that supplies this function's description; resolved server-side.

params: list[AiFunctionParam]

Parameters that the function accepts. Required.

attributes: AiFunctionAttributes

Additional attributes for the function.

categories: list[str]

Categories this function belongs to.

internal: bool

Whether this function is internal and not meant for direct use.

class AiFunctionParam(typing.TypedDict):
715class AiFunctionParam(TypedDict, total=False):
716    """Defines the structure of a parameter for an AI function."""
717
718    name: str
719    """Name of the parameter. Required."""
720    description: str
721    """Description of the parameter's purpose. Required."""
722    type: AiFunctionParamType
723    """Data type of the parameter. Required."""
724    required: bool
725    """Indicates if the parameter is mandatory. Required."""
726    enum: list[str]
727    """List of possible values for the parameter, if applicable."""

Defines the structure of a parameter for an AI function.

name: str

Name of the parameter. Required.

description: str

Description of the parameter's purpose. Required.

type: Literal['string', 'number', 'boolean', 'date', 'files']

Data type of the parameter. Required.

required: bool

Indicates if the parameter is mandatory. Required.

enum: list[str]

List of possible values for the parameter, if applicable.

class AiKnowledgeBase(typing.TypedDict):
560class AiKnowledgeBase(TypedDict, total=False):
561    """An AI knowledge base that can be attached to an AI agent.
562
563    Returned by :meth:`AiClient.list_knowledge_bases`.
564    """
565
566    id: str
567    """The unique identifier of the knowledge base. Required."""
568    appId: str
569    """The app ID that the knowledge base belongs to. Required."""
570    description: str
571    """The user's description of the knowledge base. Required."""
572    metadataFields: list[AiKnowledgeBaseMetadataField]
573    """Predefined metadata fields that can be used for filtering. Required."""
574    embeddingModel: AiEmbeddingsModelSelection
575    """The embedding model used by this knowledge base. Required."""
576    chatModel: AiChatModelSelection
577    """The model used when asking questions of this knowledge base. Required."""
578    vectorDbType: VectorDbType
579    """The vector store backend the knowledge base reads/writes from. Set at creation
580    and immutable thereafter. Absent on older records."""
581    graphRag: AiKnowledgeBaseGraphConfig
582    """Opt-in GraphRAG configuration. Only honored for ``vectorDbType: 'mongoAtlas'``
583    knowledge bases; enables per-chunk entity/relationship extraction at ingest and
584    ``searchMode: 'graph'`` at query time."""
585    updatedAt: str
586    """ISO 8601 timestamp of when the knowledge base was last updated. Required."""

An AI knowledge base that can be attached to an AI agent.

Returned by AiClient.list_knowledge_bases().

id: str

The unique identifier of the knowledge base. Required.

appId: str

The app ID that the knowledge base belongs to. Required.

description: str

The user's description of the knowledge base. Required.

metadataFields: list[AiKnowledgeBaseMetadataField]

Predefined metadata fields that can be used for filtering. Required.

embeddingModel: str | IntegrationEmbeddingModelSpec

The embedding model used by this knowledge base. Required.

chatModel: str | IntegrationModelSpec

The model used when asking questions of this knowledge base. Required.

vectorDbType: Literal['postgres', 'mongoAtlas']

The vector store backend the knowledge base reads/writes from. Set at creation and immutable thereafter. Absent on older records.

Opt-in GraphRAG configuration. Only honored for vectorDbType: 'mongoAtlas' knowledge bases; enables per-chunk entity/relationship extraction at ingest and searchMode: 'graph' at query time.

updatedAt: str

ISO 8601 timestamp of when the knowledge base was last updated. Required.

class AiKnowledgeBaseGraphConceptsConfig(typing.TypedDict):
509class AiKnowledgeBaseGraphConceptsConfig(TypedDict, total=False):
510    """Configuration of the graph's concept layer (facet taxonomies)."""
511
512    facets: Union[list[str], Literal["auto"]]
513    """Which metadata fields become value facets. ``'auto'`` (the default) profiles the KB's
514    context metadata and selects the categorical fields; an explicit list overrides
515    auto-selection; an empty list disables value facets."""
516    pathFacets: list[AiKnowledgeBaseGraphPathFacetConfig]
517    """Explicit path facets, e.g. ``[{'field': 'folderPath', 'type': 'path'}]``. A path facet
518    always builds when its field has values, independent of ``facets``."""

Configuration of the graph's concept layer (facet taxonomies).

facets: list[str] | Literal['auto']

Which metadata fields become value facets. 'auto' (the default) profiles the KB's context metadata and selects the categorical fields; an explicit list overrides auto-selection; an empty list disables value facets.

Explicit path facets, e.g. [{'field': 'folderPath', 'type': 'path'}]. A path facet always builds when its field has values, independent of facets.

class AiKnowledgeBaseGraphConfig(squidcloud.types._AiKnowledgeBaseGraphConfigRequired):
533class AiKnowledgeBaseGraphConfig(_AiKnowledgeBaseGraphConfigRequired, total=False):
534    """Per-knowledge-base GraphRAG configuration.
535
536    Opt-in and mutable (unlike ``embeddingModel``/``vectorDbType``); only honored for
537    ``vectorDbType: 'mongoAtlas'`` knowledge bases. Enabling it on a knowledge base that
538    already has content backfills the graph automatically once the KB goes quiet.
539
540    On upsert the supplied value replaces the stored config rather than merging into it,
541    so include every key that should remain set.
542    """
543
544    extractionModel: AiChatModelSelection
545    """Chat model used for per-chunk entity/relationship extraction. Defaults to the server's
546    graph model."""
547    entityTypes: list[str]
548    """Optional domain taxonomy hint (entity types) injected into the extraction prompt."""
549    autoBuildDebounceMs: int
550    """Quiet window (milliseconds) after the last graph activity before the sweep auto-builds
551    the structure. Must be a positive integer. Defaults to the server's window (5 minutes)."""
552    autoBuildPaused: bool
553    """When true, the sweep does not start automatic structural builds: activity keeps arming the
554    build, and the first quiet window after the flag is cleared runs it. ``rebuild_graph`` stays
555    available. Defaults to false."""
556    concepts: AiKnowledgeBaseGraphConceptsConfig
557    """Concept-layer (facet taxonomy) configuration. Omitted means ``facets: 'auto'``."""

Per-knowledge-base GraphRAG configuration.

Opt-in and mutable (unlike embeddingModel/vectorDbType); only honored for vectorDbType: 'mongoAtlas' knowledge bases. Enabling it on a knowledge base that already has content backfills the graph automatically once the KB goes quiet.

On upsert the supplied value replaces the stored config rather than merging into it, so include every key that should remain set.

extractionModel: str | IntegrationModelSpec

Chat model used for per-chunk entity/relationship extraction. Defaults to the server's graph model.

entityTypes: list[str]

Optional domain taxonomy hint (entity types) injected into the extraction prompt.

autoBuildDebounceMs: int

Quiet window (milliseconds) after the last graph activity before the sweep auto-builds the structure. Must be a positive integer. Defaults to the server's window (5 minutes).

autoBuildPaused: bool

When true, the sweep does not start automatic structural builds: activity keeps arming the build, and the first quiet window after the flag is cleared runs it. rebuild_graph stays available. Defaults to false.

Concept-layer (facet taxonomy) configuration. Omitted means facets: 'auto'.

class AiKnowledgeBaseGraphFilter(typing.TypedDict):
653class AiKnowledgeBaseGraphFilter(TypedDict):
654    """Graph scope for a knowledge-base search: restricts results to the documents under one
655    concept of the KB's graph. Composes with every ``searchMode``. Requires ``graphRag.enabled``."""
656
657    underConcept: str
658    """The concept to scope to: a facet nodeId (``facet_…`` — exact) or a concept name,
659    resolved server-side (exact, then alias, then similarity). An unresolvable ref fails with
660    ``CONCEPT_NOT_FOUND`` and the nearest matching concept names."""

Graph scope for a knowledge-base search: restricts results to the documents under one concept of the KB's graph. Composes with every searchMode. Requires graphRag.enabled.

underConcept: str

The concept to scope to: a facet nodeId (facet_… — exact) or a concept name, resolved server-side (exact, then alias, then similarity). An unresolvable ref fails with CONCEPT_NOT_FOUND and the nearest matching concept names.

class AiKnowledgeBaseGraphPathFacetConfig(squidcloud.types._AiKnowledgeBaseGraphPathFacetConfigRequired):
500class AiKnowledgeBaseGraphPathFacetConfig(
501    _AiKnowledgeBaseGraphPathFacetConfigRequired, total=False
502):
503    """A path facet: a deterministic tree built by splitting a hierarchical path field."""
504
505    separator: str
506    """Path segment separator. Defaults to ``'/'``."""

A path facet: a deterministic tree built by splitting a hierarchical path field.

separator: str

Path segment separator. Defaults to '/'.

class AiKnowledgeBaseGraphSearchOptions(typing.TypedDict):
639class AiKnowledgeBaseGraphSearchOptions(TypedDict, total=False):
640    """Tuning for ``searchMode: 'graph'`` (GraphRAG) retrieval. Ignored for other search modes."""
641
642    seedLimit: int
643    """Entities seeded via vector search before graph expansion. Default 8, max 25."""
644    maxHops: int
645    """Number of hops to expand from each seed (traversal depth + 1). Default 2, max 3."""
646    includeGraphContext: bool
647    """When True, the server attaches the traversed subgraph to the search response as
648    ``graphContext``. Default False. Read it via
649    :meth:`~squidcloud.ai.KnowledgeBaseClient.search_with_graph_context`;
650    :meth:`~squidcloud.ai.KnowledgeBaseClient.search` returns only the chunks."""

Tuning for searchMode: 'graph' (GraphRAG) retrieval. Ignored for other search modes.

seedLimit: int

Entities seeded via vector search before graph expansion. Default 8, max 25.

maxHops: int

Number of hops to expand from each seed (traversal depth + 1). Default 2, max 3.

includeGraphContext: bool

When True, the server attaches the traversed subgraph to the search response as graphContext. Default False. Read it via ~squidcloud.ai.KnowledgeBaseClient.search_with_graph_context(); ~squidcloud.ai.KnowledgeBaseClient.search() returns only the chunks.

class AiKnowledgeBaseMetadataField(typing.TypedDict):
457class AiKnowledgeBaseMetadataField(TypedDict, total=False):
458    """Metadata field definition for a knowledge base."""
459
460    name: str
461    dataType: str
462    required: bool
463    description: str

Metadata field definition for a knowledge base.

name: str
dataType: str
required: bool
description: str
class AiPiiOptions(typing.TypedDict):
187class AiPiiOptions(TypedDict, total=False):
188    """Refuses prompts carrying PII before they reach the agent's model.
189
190    The inverse of GuardrailsOptions.disablePii, which asks the agent's own model
191    not to emit PII in its answer.
192    """
193
194    onDetect: str
195    """'reject' refuses a prompt carrying PII; 'off' (default) disables screening."""
196    entities: list[str]
197    """Entity kinds to screen for: 'email', 'phoneNumber', 'creditCard', 'ssn', 'iban', 'passport'."""
198    customRules: list[str]
199    """App-specific PII described in plain language, screened by classifierModel."""
200    classifierModel: AiChatModelSelection
201    """Model screening customRules. Defaults to 'gpt-5.6-luna'."""
202    allowList: list[str]
203    """Literal values that never count as PII."""

Refuses prompts carrying PII before they reach the agent's model.

The inverse of GuardrailsOptions.disablePii, which asks the agent's own model not to emit PII in its answer.

onDetect: str

'reject' refuses a prompt carrying PII; 'off' (default) disables screening.

entities: list[str]

Entity kinds to screen for: 'email', 'phoneNumber', 'creditCard', 'ssn', 'iban', 'passport'.

customRules: list[str]

App-specific PII described in plain language, screened by classifierModel.

classifierModel: str | IntegrationModelSpec

Model screening customRules. Defaults to 'gpt-5.6-luna'.

allowList: list[str]

Literal values that never count as PII.

class AiQueryAnalyzeResultsOptions(typing.TypedDict):
341class AiQueryAnalyzeResultsOptions(TypedDict, total=False):
342    """Options for the result-analysis stage of AI query."""
343
344    disabled: bool
345    """When true, skip analysis and return raw results only."""
346    enableCodeInterpreter: bool
347    """Enable code interpreter mode (default false)."""
348    aiOptions: AiChatOptions
349    """Customize AI agent behavior used by the stage."""
350    agentId: AiAgentId
351    """If set, use this agent to analyze results and produce the final answer."""

Options for the result-analysis stage of AI query.

disabled: bool

When true, skip analysis and return raw results only.

enableCodeInterpreter: bool

Enable code interpreter mode (default false).

aiOptions: AiChatOptions

Customize AI agent behavior used by the stage.

agentId: str

If set, use this agent to analyze results and produce the final answer.

AiQueryCollectionsSelectionRunMode = typing.Literal['default', 'force', 'disable']
class AiQueryGenerateQueryOptions(typing.TypedDict):
327class AiQueryGenerateQueryOptions(TypedDict, total=False):
328    """Options for the query-generation stage of AI query."""
329
330    aiOptions: AiChatOptions
331    """Customize AI agent behavior used by the stage."""
332    maxErrorCorrections: int
333    """Number of retries due to errors in a generated AI query (default 2)."""
334    agentId: AiAgentId
335    """If set, use this agent to generate the query."""
336    allowClarification: bool
337    """When true, allow the AI to ask a clarifying question instead of
338    generating a query for ambiguous prompts (default false)."""

Options for the query-generation stage of AI query.

aiOptions: AiChatOptions

Customize AI agent behavior used by the stage.

maxErrorCorrections: int

Number of retries due to errors in a generated AI query (default 2).

agentId: str

If set, use this agent to generate the query.

allowClarification: bool

When true, allow the AI to ask a clarifying question instead of generating a query for ambiguous prompts (default false).

class AiQueryOptions(typing.TypedDict):
363class AiQueryOptions(TypedDict, total=False):
364    """Options for configuring AI query execution.
365
366    Mirrors ``AiQueryOptions`` from the TypeScript SDK. All fields optional.
367    """
368
369    instructions: str
370    """Custom instructions applied to all stages unless overridden per-stage."""
371    enableRawResults: bool
372    """Enable raw results output."""
373    selectCollectionsOptions: AiQuerySelectCollectionsOptions
374    """Collection-selection stage options."""
375    generateQueryOptions: AiQueryGenerateQueryOptions
376    """Query-generation stage options."""
377    analyzeResultsOptions: AiQueryAnalyzeResultsOptions
378    """Result-analysis stage options."""
379    sessionContext: AiSessionContext
380    """Session information (``agentId`` may be omitted)."""
381    memoryOptions: AiAgentMemoryOptions
382    """Memory/session configuration."""
383    generateQueriesOnly: bool
384    """If true, return generated queries without executing or analyzing them."""
385    validateWithAiOptions: AiQueryValidateWithAiOptions
386    """Optional AI validation of generated queries."""

Options for configuring AI query execution.

Mirrors AiQueryOptions from the TypeScript SDK. All fields optional.

instructions: str

Custom instructions applied to all stages unless overridden per-stage.

enableRawResults: bool

Enable raw results output.

selectCollectionsOptions: AiQuerySelectCollectionsOptions

Collection-selection stage options.

generateQueryOptions: AiQueryGenerateQueryOptions

Query-generation stage options.

analyzeResultsOptions: AiQueryAnalyzeResultsOptions

Result-analysis stage options.

sessionContext: AiSessionContext

Session information (agentId may be omitted).

memoryOptions: AiAgentMemoryOptions

Memory/session configuration.

generateQueriesOnly: bool

If true, return generated queries without executing or analyzing them.

validateWithAiOptions: AiQueryValidateWithAiOptions

Optional AI validation of generated queries.

class AiQuerySelectCollectionsOptions(typing.TypedDict):
316class AiQuerySelectCollectionsOptions(TypedDict, total=False):
317    """Options for the collection-selection stage of AI query."""
318
319    collectionsToUse: list[str]
320    """Restrict query to these collections. Defaults to all collections."""
321    runMode: AiQueryCollectionsSelectionRunMode
322    """Stage behavior: 'default', 'force', or 'disable'."""
323    aiOptions: AiChatOptions
324    """Customize AI agent behavior used by the stage."""

Options for the collection-selection stage of AI query.

collectionsToUse: list[str]

Restrict query to these collections. Defaults to all collections.

runMode: Literal['default', 'force', 'disable']

Stage behavior: 'default', 'force', or 'disable'.

aiOptions: AiChatOptions

Customize AI agent behavior used by the stage.

class AiQueryValidateWithAiOptions(typing.TypedDict):
354class AiQueryValidateWithAiOptions(TypedDict, total=False):
355    """Options for AI-based validation of generated queries."""
356
357    enabled: bool
358    """Whether AI validation is enabled."""
359    aiOptions: AiChatOptions
360    """Defaults to the same model used for query generation."""

Options for AI-based validation of generated queries.

enabled: bool

Whether AI validation is enabled.

aiOptions: AiChatOptions

Defaults to the same model used for query generation.

class AiSessionContext(typing.TypedDict):
303class AiSessionContext(TypedDict, total=False):
304    """Session context for AI query execution.
305
306    Mirrors the TypeScript ``AiSessionContext`` type. All fields optional
307    here because the query endpoint accepts a partial context (``agentId``
308    may be omitted).
309    """
310
311    clientId: str
312    agentId: str
313    jobId: str

Session context for AI query execution.

Mirrors the TypeScript AiSessionContext type. All fields optional here because the query endpoint accepts a partial context (agentId may be omitted).

clientId: str
agentId: str
jobId: str
class AiStructuredOutputFormat(typing.TypedDict):
150class AiStructuredOutputFormat(TypedDict):
151    """Structured output format for JSON responses."""
152
153    type: Literal["json_schema"]
154    schema: dict[str, Any]

Structured output format for JSON responses.

type: Literal['json_schema']
schema: dict[str, typing.Any]
class AudioClient:
 947class AudioClient:
 948    """Audio transcription and speech synthesis.
 949
 950    Obtained via :meth:`AiClient.audio`.
 951
 952    Example::
 953
 954        text = await squid.ai().audio().transcribe(audio_bytes)
 955        speech = await squid.ai().audio().create_speech("Hello!", options={"voice": "nova"})
 956    """
 957
 958    def __init__(self, http: HttpTransport) -> None:
 959        self._http = http
 960
 961    async def transcribe(
 962        self,
 963        audio_data: bytes,
 964        filename: str = "audio.wav",
 965        content_type: str = "audio/wav",
 966        *,
 967        options: dict[str, Any] | None = None,
 968    ) -> str:
 969        """Transcribe audio to text.
 970
 971        Args:
 972            audio_data: The audio file content as bytes.
 973            filename: The filename (used in the multipart upload).
 974            content_type: The MIME type of the audio file.
 975            options: Provider-specific transcription options.
 976
 977        Returns:
 978            The transcribed text.
 979        """
 980        form_data: dict[str, str] = {
 981            "optionsJson": json.dumps(options or {}),
 982        }
 983        result = await self._http.post_form(
 984            "squid-api/v1/ai/audio/transcribe",
 985            data=form_data,
 986            files=[("file", (filename, audio_data, content_type))],
 987        )
 988        return result if isinstance(result, str) else str(result)
 989
 990    async def create_speech(
 991        self,
 992        text: str,
 993        options: AiAudioCreateSpeechOptions,
 994    ) -> bytes:
 995        """Generate speech audio from text.
 996
 997        Args:
 998            text: The text to convert to speech.
 999            options: Speech generation options. See :class:`AiAudioCreateSpeechOptions`.
1000                Required keys: ``modelName`` (e.g., ``'tts-1'``), ``voice``
1001                (e.g., ``'nova'``, ``'alloy'``).
1002
1003        Returns:
1004            Raw audio file bytes (e.g., MP3 format by default).
1005
1006        Example::
1007
1008            audio_data = (
1009                await squid.ai()
1010                .audio()
1011                .create_speech(
1012                    "Hello!",
1013                    options={
1014                        "modelName": "tts-1",
1015                        "voice": "nova",
1016                    },
1017                )
1018            )
1019            with open("speech.mp3", "wb") as f:
1020                f.write(audio_data)
1021        """
1022        return await self._http.post(
1023            "squid-api/v1/ai/audio/createSpeech",
1024            {"input": text, "options": options},
1025        )

Audio transcription and speech synthesis.

Obtained via AiClient.audio().

Example::

text = await squid.ai().audio().transcribe(audio_bytes)
speech = await squid.ai().audio().create_speech("Hello!", options={"voice": "nova"})
AudioClient(http: squidcloud.http.HttpTransport)
958    def __init__(self, http: HttpTransport) -> None:
959        self._http = http
async def transcribe( self, audio_data: bytes, filename: str = 'audio.wav', content_type: str = 'audio/wav', *, options: dict[str, Any] | None = None) -> str:
961    async def transcribe(
962        self,
963        audio_data: bytes,
964        filename: str = "audio.wav",
965        content_type: str = "audio/wav",
966        *,
967        options: dict[str, Any] | None = None,
968    ) -> str:
969        """Transcribe audio to text.
970
971        Args:
972            audio_data: The audio file content as bytes.
973            filename: The filename (used in the multipart upload).
974            content_type: The MIME type of the audio file.
975            options: Provider-specific transcription options.
976
977        Returns:
978            The transcribed text.
979        """
980        form_data: dict[str, str] = {
981            "optionsJson": json.dumps(options or {}),
982        }
983        result = await self._http.post_form(
984            "squid-api/v1/ai/audio/transcribe",
985            data=form_data,
986            files=[("file", (filename, audio_data, content_type))],
987        )
988        return result if isinstance(result, str) else str(result)

Transcribe audio to text.

Arguments:
  • audio_data: The audio file content as bytes.
  • filename: The filename (used in the multipart upload).
  • content_type: The MIME type of the audio file.
  • options: Provider-specific transcription options.
Returns:

The transcribed text.

async def create_speech( self, text: str, options: AiAudioCreateSpeechOptions) -> bytes:
 990    async def create_speech(
 991        self,
 992        text: str,
 993        options: AiAudioCreateSpeechOptions,
 994    ) -> bytes:
 995        """Generate speech audio from text.
 996
 997        Args:
 998            text: The text to convert to speech.
 999            options: Speech generation options. See :class:`AiAudioCreateSpeechOptions`.
1000                Required keys: ``modelName`` (e.g., ``'tts-1'``), ``voice``
1001                (e.g., ``'nova'``, ``'alloy'``).
1002
1003        Returns:
1004            Raw audio file bytes (e.g., MP3 format by default).
1005
1006        Example::
1007
1008            audio_data = (
1009                await squid.ai()
1010                .audio()
1011                .create_speech(
1012                    "Hello!",
1013                    options={
1014                        "modelName": "tts-1",
1015                        "voice": "nova",
1016                    },
1017                )
1018            )
1019            with open("speech.mp3", "wb") as f:
1020                f.write(audio_data)
1021        """
1022        return await self._http.post(
1023            "squid-api/v1/ai/audio/createSpeech",
1024            {"input": text, "options": options},
1025        )

Generate speech audio from text.

Arguments:
  • text: The text to convert to speech.
  • options: Speech generation options. See AiAudioCreateSpeechOptions. Required keys: modelName (e.g., 'tts-1'), voice (e.g., 'nova', 'alloy').
Returns:

Raw audio file bytes (e.g., MP3 format by default).

Example::

audio_data = (
    await squid.ai()
    .audio()
    .create_speech(
        "Hello!",
        options={
            "modelName": "tts-1",
            "voice": "nova",
        },
    )
)
with open("speech.mp3", "wb") as f:
    f.write(audio_data)
class CreatePdfDimensionsOptions(typing.TypedDict):
867class CreatePdfDimensionsOptions(TypedDict):
868    """PDF output options by custom dimensions."""
869
870    type: Literal["dimensions"]
871    width: int
872    """Width in pixels."""
873    height: int
874    """Height in pixels."""

PDF output options by custom dimensions.

type: Literal['dimensions']
width: int

Width in pixels.

height: int

Height in pixels.

class CreatePdfFormatOptions(typing.TypedDict):
848class CreatePdfFormatOptions(TypedDict):
849    """PDF output options by format."""
850
851    type: Literal["format"]
852    format: Literal[
853        "letter",
854        "legal",
855        "tabloid",
856        "ledger",
857        "a0",
858        "a1",
859        "a2",
860        "a3",
861        "a4",
862        "a5",
863        "a6",
864    ]

PDF output options by format.

type: Literal['format']
format: Literal['letter', 'legal', 'tabloid', 'ledger', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6']
class EmbeddingModelIdSpec(typing.TypedDict):
68class EmbeddingModelIdSpec(TypedDict, total=False):
69    """An embedding model a new knowledge base can be created on.
70
71    Returned by :meth:`AiClient.list_embedding_models`.
72    """
73
74    modelId: str
75    """The embedding model ID used for API calls. Required."""
76    integrationId: str
77    """The integration ID if this model comes from an integration. Absent for Squid-provided models."""
78    dimensions: int
79    """The embedding dimensions, present for integration-based models."""
80    displayName: str
81    """Human-readable display name for the model. Required."""
82    source: AiModelSource
83    """Where the model comes from: 'vendor' (Squid-provided) or 'connector' (an integration
84    configured on the app). Required."""
85    replacedBy: str
86    """Set only on a deprecated vendor model, listed on request: the model a new knowledge base
87    asking for this one is created on. Absent for active models."""
88    isDefault: bool
89    """True on exactly one entry of a non-empty list: the model to create on with no preference."""

An embedding model a new knowledge base can be created on.

Returned by AiClient.list_embedding_models().

modelId: str

The embedding model ID used for API calls. Required.

integrationId: str

The integration ID if this model comes from an integration. Absent for Squid-provided models.

dimensions: int

The embedding dimensions, present for integration-based models.

displayName: str

Human-readable display name for the model. Required.

source: Literal['vendor', 'connector', 'custom']

Where the model comes from: 'vendor' (Squid-provided) or 'connector' (an integration configured on the app). Required.

replacedBy: str

Set only on a deprecated vendor model, listed on request: the model a new knowledge base asking for this one is created on. Absent for active models.

isDefault: bool

True on exactly one entry of a non-empty list: the model to create on with no preference.

class ExtractDataFromDocumentOptions(typing.TypedDict):
880class ExtractDataFromDocumentOptions(TypedDict, total=False):
881    """Options for document data extraction."""
882
883    extractImages: bool
884    """Whether to extract embedded images. Defaults to True."""
885    imageMinSizePixels: int
886    """Minimum image size to extract."""
887    pageIndexes: list[int]
888    """Specific pages to extract (0-based)."""
889    preferredExtractionMethod: str
890    discardOriginalFile: bool

Options for document data extraction.

extractImages: bool

Whether to extract embedded images. Defaults to True.

imageMinSizePixels: int

Minimum image size to extract.

pageIndexes: list[int]

Specific pages to extract (0-based).

preferredExtractionMethod: str
discardOriginalFile: bool
class ExtractionClient:
17class ExtractionClient:
18    """Document extraction and PDF creation."""
19
20    def __init__(self, http: HttpTransport) -> None:
21        self._http = http
22
23    async def create_pdf_from_html(
24        self,
25        inner_html: str,
26        *,
27        title: str | None = None,
28        css_url: str | None = None,
29        output_options: CreatePdfOutputOptions | None = None,
30    ) -> dict:
31        """Create a PDF from HTML content.
32
33        Returns CreatePdfResponse with 'url' and 'fileName'.
34        """
35        body: dict[str, Any] = {"type": "html", "innerHtml": inner_html}
36        if title is not None:
37            body["title"] = title
38        if css_url is not None:
39            body["cssUrl"] = css_url
40        if output_options is not None:
41            body["outputOptions"] = output_options
42        return await self._http.post("squid-api/v1/extraction/createPdf", body)
43
44    async def create_pdf_from_url(
45        self,
46        url: str,
47        *,
48        title: str | None = None,
49        output_options: CreatePdfOutputOptions | None = None,
50    ) -> dict:
51        """Create a PDF from a URL.
52
53        Returns CreatePdfResponse with 'url' and 'fileName'.
54        """
55        body: dict[str, Any] = {"type": "url", "url": url}
56        if title is not None:
57            body["title"] = title
58        if output_options is not None:
59            body["outputOptions"] = output_options
60        return await self._http.post("squid-api/v1/extraction/createPdf", body)
61
62    async def extract_data_from_document_url(
63        self,
64        url: str,
65        options: ExtractDataFromDocumentOptions | None = None,
66    ) -> dict:
67        """Extract structured data from a document URL.
68
69        Returns ExtractDataFromDocumentResponse with 'pages' and optional 'longTermStoragePath'.
70        """
71        body: dict[str, Any] = {"url": url}
72        if options is not None:
73            body["options"] = options
74        return await self._http.post("squid-api/v1/extraction/extractDataFromDocumentUrl", body)
75
76    async def extract_data_from_document_file(
77        self,
78        file_data: bytes,
79        filename: str,
80        content_type: str = "application/pdf",
81        options: ExtractDataFromDocumentOptions | None = None,
82    ) -> dict:
83        """Extract structured data from a document file.
84
85        Returns ExtractDataFromDocumentResponse with 'pages' and optional 'longTermStoragePath'.
86        """
87        request: dict[str, Any] = {}
88        if options is not None:
89            request["options"] = options
90        form_data = {"request": json.dumps(request)}
91
92        return await self._http.post_form(
93            "squid-api/v1/extraction/extractDataFromDocumentFile",
94            data=form_data,
95            files=[("file", (filename, file_data, content_type))],
96        )

Document extraction and PDF creation.

ExtractionClient(http: squidcloud.http.HttpTransport)
20    def __init__(self, http: HttpTransport) -> None:
21        self._http = http
async def create_pdf_from_html( self, inner_html: str, *, title: str | None = None, css_url: str | None = None, output_options: CreatePdfFormatOptions | CreatePdfDimensionsOptions | None = None) -> dict:
23    async def create_pdf_from_html(
24        self,
25        inner_html: str,
26        *,
27        title: str | None = None,
28        css_url: str | None = None,
29        output_options: CreatePdfOutputOptions | None = None,
30    ) -> dict:
31        """Create a PDF from HTML content.
32
33        Returns CreatePdfResponse with 'url' and 'fileName'.
34        """
35        body: dict[str, Any] = {"type": "html", "innerHtml": inner_html}
36        if title is not None:
37            body["title"] = title
38        if css_url is not None:
39            body["cssUrl"] = css_url
40        if output_options is not None:
41            body["outputOptions"] = output_options
42        return await self._http.post("squid-api/v1/extraction/createPdf", body)

Create a PDF from HTML content.

Returns CreatePdfResponse with 'url' and 'fileName'.

async def create_pdf_from_url( self, url: str, *, title: str | None = None, output_options: CreatePdfFormatOptions | CreatePdfDimensionsOptions | None = None) -> dict:
44    async def create_pdf_from_url(
45        self,
46        url: str,
47        *,
48        title: str | None = None,
49        output_options: CreatePdfOutputOptions | None = None,
50    ) -> dict:
51        """Create a PDF from a URL.
52
53        Returns CreatePdfResponse with 'url' and 'fileName'.
54        """
55        body: dict[str, Any] = {"type": "url", "url": url}
56        if title is not None:
57            body["title"] = title
58        if output_options is not None:
59            body["outputOptions"] = output_options
60        return await self._http.post("squid-api/v1/extraction/createPdf", body)

Create a PDF from a URL.

Returns CreatePdfResponse with 'url' and 'fileName'.

async def extract_data_from_document_url( self, url: str, options: ExtractDataFromDocumentOptions | None = None) -> dict:
62    async def extract_data_from_document_url(
63        self,
64        url: str,
65        options: ExtractDataFromDocumentOptions | None = None,
66    ) -> dict:
67        """Extract structured data from a document URL.
68
69        Returns ExtractDataFromDocumentResponse with 'pages' and optional 'longTermStoragePath'.
70        """
71        body: dict[str, Any] = {"url": url}
72        if options is not None:
73            body["options"] = options
74        return await self._http.post("squid-api/v1/extraction/extractDataFromDocumentUrl", body)

Extract structured data from a document URL.

Returns ExtractDataFromDocumentResponse with 'pages' and optional 'longTermStoragePath'.

async def extract_data_from_document_file( self, file_data: bytes, filename: str, content_type: str = 'application/pdf', options: ExtractDataFromDocumentOptions | None = None) -> dict:
76    async def extract_data_from_document_file(
77        self,
78        file_data: bytes,
79        filename: str,
80        content_type: str = "application/pdf",
81        options: ExtractDataFromDocumentOptions | None = None,
82    ) -> dict:
83        """Extract structured data from a document file.
84
85        Returns ExtractDataFromDocumentResponse with 'pages' and optional 'longTermStoragePath'.
86        """
87        request: dict[str, Any] = {}
88        if options is not None:
89            request["options"] = options
90        form_data = {"request": json.dumps(request)}
91
92        return await self._http.post_form(
93            "squid-api/v1/extraction/extractDataFromDocumentFile",
94            data=form_data,
95            files=[("file", (filename, file_data, content_type))],
96        )

Extract structured data from a document file.

Returns ExtractDataFromDocumentResponse with 'pages' and optional 'longTermStoragePath'.

class FileContextRequest(typing.TypedDict):
616class FileContextRequest(TypedDict, total=False):
617    """Request to upsert a file context."""
618
619    contextId: str
620    type: Literal["file"]
621    metadata: dict[str, Any]
622    extractImages: bool
623    """Whether to extract and describe images in the file. Defaults to True.
624
625    This is the opposite of the text context default, which is False: a file's charts and
626    scanned pages carry content its text layer does not, while images referenced from pasted
627    markdown/HTML are usually layout chrome.
628    """
629    imageMinSizePixels: int
630    extractionModel: AiChatModelSelection
631    options: AiContextFileOptions
632    preferredExtractionMethod: str
633    discardOriginalFile: bool

Request to upsert a file context.

contextId: str
type: Literal['file']
metadata: dict[str, typing.Any]
extractImages: bool

Whether to extract and describe images in the file. Defaults to True.

This is the opposite of the text context default, which is False: a file's charts and scanned pages carry content its text layer does not, while images referenced from pasted markdown/HTML are usually layout chrome.

imageMinSizePixels: int
extractionModel: str | IntegrationModelSpec
preferredExtractionMethod: str
discardOriginalFile: bool
class FluxOptions(typing.TypedDict):
780class FluxOptions(TypedDict, total=False):
781    """Options for Flux image generation."""
782
783    modelName: Literal["flux-pro-1.1", "flux-kontext-pro"]
784    width: int
785    """Must be multiple of 32, min 256, max 1440."""
786    height: int
787    """Must be multiple of 32, min 256, max 1440."""
788    prompt_upsampling: bool
789    seed: int
790    safety_tolerance: int
791    """1 (strict) to 5 (permissive)."""

Options for Flux image generation.

modelName: Literal['flux-pro-1.1', 'flux-kontext-pro']
width: int

Must be multiple of 32, min 256, max 1440.

height: int

Must be multiple of 32, min 256, max 1440.

prompt_upsampling: bool
seed: int
safety_tolerance: int

1 (strict) to 5 (permissive).

class GptImageOptions(typing.TypedDict):
764class GptImageOptions(TypedDict, total=False):
765    """Options for OpenAI gpt-image-* family image generation."""
766
767    modelName: Literal[
768        "gpt-image-1",
769        "gpt-image-1-mini",
770        "gpt-image-1.5",
771        "gpt-image-2",
772        "gpt-image-2-2026-04-21",
773        "chatgpt-image-latest",
774    ]
775    quality: Literal["auto", "high", "medium", "low"]
776    size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"]
777    numberOfImagesToGenerate: int

Options for OpenAI gpt-image-* family image generation.

modelName: Literal['gpt-image-1', 'gpt-image-1-mini', 'gpt-image-1.5', 'gpt-image-2', 'gpt-image-2-2026-04-21', 'chatgpt-image-latest']
quality: Literal['auto', 'high', 'medium', 'low']
size: Literal['1024x1024', '1024x1536', '1536x1024', 'auto']
numberOfImagesToGenerate: int
class GuardrailsOptions(typing.TypedDict):
172class GuardrailsOptions(TypedDict, total=False):
173    """Guardrail options for agent responses."""
174
175    custom: str
176    """A custom guardrail instruction."""
177    disablePii: bool
178    """Disables personally identifiable information if true."""
179    professionalTone: bool
180    """Enforces a professional tone if true."""
181    offTopicAnswers: bool
182    """Prevents off-topic answers if true."""
183    disableProfanity: bool
184    """Disables profanity if true."""

Guardrail options for agent responses.

custom: str

A custom guardrail instruction.

disablePii: bool

Disables personally identifiable information if true.

professionalTone: bool

Enforces a professional tone if true.

offTopicAnswers: bool

Prevents off-topic answers if true.

disableProfanity: bool

Disables profanity if true.

class ImageClient:
873class ImageClient:
874    """Image generation and processing.
875
876    Supports DALL-E, Stable Diffusion Core, and Flux models.
877
878    Obtained via :meth:`AiClient.image`.
879
880    Example::
881
882        image_url = (
883            await squid.ai()
884            .image()
885            .generate(
886                "a cat astronaut on the moon",
887                options={"modelName": "gpt-image-1", "quality": "high"},
888            )
889        )
890    """
891
892    def __init__(self, http: HttpTransport) -> None:
893        self._http = http
894
895    async def generate(
896        self,
897        prompt: str,
898        options: ImageGenerateOptions | None = None,
899    ) -> str:
900        """Generate an image from a text prompt.
901
902        Args:
903            prompt: A text description of the image to generate.
904            options: Provider-specific generation options. Use one of:
905                - :class:`GptImageOptions`: ``{'modelName': 'gpt-image-1', 'quality': 'high', 'size': '1024x1024'}``
906                - :class:`FluxOptions`: ``{'modelName': 'flux-pro-1.1', 'width': 1024, 'height': 768}``
907                - :class:`StableDiffusionOptions`: ``{'modelName': 'stable-diffusion-core', 'aspectRatio': '16:9'}``
908
909        Returns:
910            The URL of the generated image.
911        """
912        result = await self._http.post(
913            "squid-api/v1/ai/image/generate",
914            {"prompt": prompt, "options": options or {}},
915        )
916        return result if isinstance(result, str) else str(result)
917
918    async def remove_background(
919        self,
920        image_data: bytes,
921        filename: str = "image.png",
922        content_type: str = "image/png",
923    ) -> str:
924        """Remove the background from an image.
925
926        Args:
927            image_data: The image file content as bytes.
928            filename: The filename (used in the multipart upload).
929            content_type: The MIME type of the image.
930
931        Returns:
932            The URL of the processed image with the background removed.
933        """
934        result = await self._http.post_form(
935            "squid-api/v1/ai/image/removeBackground",
936            data={},
937            files=[("file", (filename, image_data, content_type))],
938        )
939        return result if isinstance(result, str) else str(result)

Image generation and processing.

Supports DALL-E, Stable Diffusion Core, and Flux models.

Obtained via AiClient.image().

Example::

image_url = (
    await squid.ai()
    .image()
    .generate(
        "a cat astronaut on the moon",
        options={"modelName": "gpt-image-1", "quality": "high"},
    )
)
ImageClient(http: squidcloud.http.HttpTransport)
892    def __init__(self, http: HttpTransport) -> None:
893        self._http = http
async def generate( self, prompt: str, options: GptImageOptions | FluxOptions | StableDiffusionOptions | None = None) -> str:
895    async def generate(
896        self,
897        prompt: str,
898        options: ImageGenerateOptions | None = None,
899    ) -> str:
900        """Generate an image from a text prompt.
901
902        Args:
903            prompt: A text description of the image to generate.
904            options: Provider-specific generation options. Use one of:
905                - :class:`GptImageOptions`: ``{'modelName': 'gpt-image-1', 'quality': 'high', 'size': '1024x1024'}``
906                - :class:`FluxOptions`: ``{'modelName': 'flux-pro-1.1', 'width': 1024, 'height': 768}``
907                - :class:`StableDiffusionOptions`: ``{'modelName': 'stable-diffusion-core', 'aspectRatio': '16:9'}``
908
909        Returns:
910            The URL of the generated image.
911        """
912        result = await self._http.post(
913            "squid-api/v1/ai/image/generate",
914            {"prompt": prompt, "options": options or {}},
915        )
916        return result if isinstance(result, str) else str(result)

Generate an image from a text prompt.

Arguments:
  • prompt: A text description of the image to generate.
  • options: Provider-specific generation options. Use one of:
    • GptImageOptions: {'modelName': 'gpt-image-1', 'quality': 'high', 'size': '1024x1024'}
    • FluxOptions: {'modelName': 'flux-pro-1.1', 'width': 1024, 'height': 768}
    • StableDiffusionOptions: {'modelName': 'stable-diffusion-core', 'aspectRatio': '16:9'}
Returns:

The URL of the generated image.

async def remove_background( self, image_data: bytes, filename: str = 'image.png', content_type: str = 'image/png') -> str:
918    async def remove_background(
919        self,
920        image_data: bytes,
921        filename: str = "image.png",
922        content_type: str = "image/png",
923    ) -> str:
924        """Remove the background from an image.
925
926        Args:
927            image_data: The image file content as bytes.
928            filename: The filename (used in the multipart upload).
929            content_type: The MIME type of the image.
930
931        Returns:
932            The URL of the processed image with the background removed.
933        """
934        result = await self._http.post_form(
935            "squid-api/v1/ai/image/removeBackground",
936            data={},
937            files=[("file", (filename, image_data, content_type))],
938        )
939        return result if isinstance(result, str) else str(result)

Remove the background from an image.

Arguments:
  • image_data: The image file content as bytes.
  • filename: The filename (used in the multipart upload).
  • content_type: The MIME type of the image.
Returns:

The URL of the processed image with the background removed.

class IntegrationEmbeddingModelSpec(typing.TypedDict):
466class IntegrationEmbeddingModelSpec(TypedDict):
467    """Specifies an embedding model from a specific integration."""
468
469    integrationId: str
470    """The ID of the integration providing the embedding model."""
471    model: str
472    """The model name as recognized by the provider."""
473    dimensions: int
474    """The number of dimensions in the embedding vector output."""

Specifies an embedding model from a specific integration.

integrationId: str

The ID of the integration providing the embedding model.

model: str

The model name as recognized by the provider.

dimensions: int

The number of dimensions in the embedding vector output.

class IntegrationModelSpec(typing.TypedDict):
31class IntegrationModelSpec(TypedDict):
32    """Specifies a model from a specific integration."""
33
34    integrationId: str
35    model: str

Specifies a model from a specific integration.

integrationId: str
model: str
class KnowledgeBaseClient:
473class KnowledgeBaseClient:
474    """Operations on a single knowledge base.
475
476    Provides methods for managing knowledge base configuration, upserting
477    and searching text/file contexts, and retrieving individual contexts.
478
479    Obtained via :meth:`AiClient.knowledge_base`.
480
481    Example::
482
483        kb = squid.ai().knowledge_base("my-kb")
484        await kb.upsert(description="Product documentation")
485        await kb.upsert_contexts(
486            [
487                {
488                    "contextId": "doc-1",
489                    "type": "text",
490                    "title": "Getting Started",
491                    "text": "Welcome to our product...",
492                }
493            ]
494        )
495        results = await kb.search("How do I get started?")
496    """
497
498    def __init__(self, http: HttpTransport, kb_id: str) -> None:
499        self._http = http
500        self._kb_id = kb_id
501
502    async def get(self) -> dict | None:
503        """Get the knowledge base details.
504
505        Returns:
506            An ``AiKnowledgeBase`` dict with keys: ``id``, ``appId``,
507            ``description``, ``metadataFields``, ``embeddingModel``,
508            ``chatModel``, ``updatedAt``. Returns ``None`` if not found.
509        """
510        return await self._http.get(f"squid-api/v1/ai/knowledge-base/get/{self._kb_id}")
511
512    async def upsert(
513        self,
514        *,
515        description: str | None = None,
516        metadata_fields: list[AiKnowledgeBaseMetadataField] | None = None,
517        embedding_model: str | None = None,
518        chat_model: AiChatModelSelection | None = None,
519        name: str | None = None,
520        vector_db_type: VectorDbType | None = None,
521        graph_rag: AiKnowledgeBaseGraphConfig | None = None,
522    ) -> None:
523        """Create or update the knowledge base.
524
525        Args:
526            description: Description of the knowledge base's content.
527            metadata_fields: Schema for metadata fields used in filtering.
528                Each field: ``{'name': str, 'dataType': str, 'required': bool, 'description'?: str}``.
529            embedding_model: The embedding model name for vectorization.
530                Required when creating a new knowledge base.
531            chat_model: The LLM model for answering questions over this KB.
532            name: Display name for the knowledge base.
533            vector_db_type: The vector store backend. Set at creation (defaulting to the
534                server's default) and immutable thereafter. The knowledge graph requires
535                ``'mongoAtlas'``.
536            graph_rag: Opt-in GraphRAG configuration (``{'enabled': True, ...}``). Only
537                honored on ``'mongoAtlas'`` knowledge bases; mutable, unlike
538                ``vector_db_type``. The value replaces the stored config rather than
539                merging into it. See :class:`AiKnowledgeBaseGraphConfig`.
540        """
541        kb: dict[str, Any] = {"id": self._kb_id}
542        if description is not None:
543            kb["description"] = description
544        if metadata_fields is not None:
545            kb["metadataFields"] = metadata_fields
546        if embedding_model is not None:
547            kb["embeddingModel"] = embedding_model
548        if chat_model is not None:
549            kb["chatModel"] = chat_model
550        if name is not None:
551            kb["name"] = name
552        if vector_db_type is not None:
553            kb["vectorDbType"] = vector_db_type
554        if graph_rag is not None:
555            kb["graphRag"] = graph_rag
556        await self._http.post("squid-api/v1/ai/knowledge-base/upsert", {"knowledgeBase": kb})
557
558    async def delete(self) -> None:
559        """Delete the knowledge base and all its contexts permanently."""
560        await self._http.post("squid-api/v1/ai/knowledge-base/delete", {"id": self._kb_id})
561
562    # --- Contexts ---
563
564    async def get_context(self, context_id: str) -> dict | None:
565        """Get a specific context entry.
566
567        Args:
568            context_id: The unique context identifier.
569
570        Returns:
571            An ``AiKnowledgeBaseContext`` dict with keys: ``id``, ``appId``,
572            ``knowledgeBaseId``, ``createdAt``, ``updatedAt``, ``type``,
573            ``title``, ``text``, ``preview``, ``sizeBytes``, ``metadata``,
574            ``requestConfig``. Returns ``None`` if not found.
575        """
576        return await self._http.get(
577            f"squid-api/v1/ai/knowledge-base/getContext/{self._kb_id}/{context_id}"
578        )
579
580    async def list_contexts(self) -> list[dict]:
581        """List all contexts in the knowledge base.
582
583        Deprecated: fetches every context in one call with no pagination — expensive for large
584        knowledge bases. Use :meth:`list_contexts_page` instead, which supports
585        ``offset``/``limit``/``search``.
586
587        Returns:
588            A list of ``AiKnowledgeBaseContext`` dicts.
589        """
590        result = await self._http.get(f"squid-api/v1/ai/knowledge-base/listContexts/{self._kb_id}")
591        return result.get("contexts", []) if result else []
592
593    async def list_contexts_page(
594        self,
595        *,
596        offset: int | None = None,
597        limit: int | None = None,
598        search: str | None = None,
599    ) -> dict:
600        """List a page of contexts in the knowledge base.
601
602        Args:
603            offset: The number of contexts to skip, for pagination.
604            limit: The maximum number of contexts to return, for pagination.
605            search: Case-insensitive substring search across id/title only.
606
607        Returns:
608            A dict with ``contexts`` (a list of ``AiKnowledgeBaseContext`` dicts for the
609            requested page) and ``totalCount`` (the total number of contexts in the
610            knowledge base, ignoring ``offset``/``limit``).
611        """
612        params = {
613            key: str(value)
614            for key, value in {"offset": offset, "limit": limit, "search": search}.items()
615            if value is not None
616        }
617        result = await self._http.get(
618            f"squid-api/v1/ai/knowledge-base/listContextsPage/{self._kb_id}",
619            params=params or None,
620        )
621        return result if result else {"contexts": [], "totalCount": 0}
622
623    async def upsert_contexts(
624        self,
625        contexts: list[ContextRequest],
626        files: list[tuple[str, bytes, str]] | None = None,
627    ) -> dict:
628        """Add or update contexts in the knowledge base.
629
630        Args:
631            contexts: A list of context request objects. Each must be either:
632                - A :class:`TextContextRequest`: ``{'contextId', 'type': 'text', 'title', 'text', ...}``
633                - A :class:`FileContextRequest`: ``{'contextId', 'type': 'file', ...}``
634            files: For file contexts, provide the actual file data as a list of
635                ``(filename, data_bytes, content_type)`` tuples. Must match the
636                order of file contexts in the ``contexts`` list.
637
638        Returns:
639            A dict with ``failures``: a list of ``UpsertContextStatusError`` dicts
640            for any contexts that failed to upsert.
641
642        Example::
643
644            await kb.upsert_contexts(
645                [
646                    {"contextId": "doc-1", "type": "text", "title": "FAQ", "text": "..."},
647                    {"contextId": "doc-2", "type": "file"},
648                ],
649                files=[
650                    ("manual.pdf", pdf_bytes, "application/pdf"),
651                ],
652            )
653        """
654        form_data = {
655            "knowledgeBaseId": self._kb_id,
656            "contexts": json.dumps(contexts),
657        }
658        file_tuples: list[tuple[str, tuple[str, bytes, str]]] = []
659        if files:
660            for fname, fdata, ftype in files:
661                file_tuples.append(("files", (fname, fdata, ftype)))
662        return await self._http.post_form(
663            "squid-api/v1/ai/knowledge-base/upsertContexts",
664            data=form_data,
665            files=file_tuples,
666        )
667
668    async def delete_contexts(self, context_ids: list[str]) -> None:
669        """Delete contexts by their IDs.
670
671        Args:
672            context_ids: List of context IDs to delete.
673        """
674        await self._http.post(
675            "squid-api/v1/ai/knowledge-base/deleteContexts",
676            {"knowledgeBaseId": self._kb_id, "contextIds": context_ids},
677        )
678
679    # --- Search ---
680
681    async def search(
682        self,
683        prompt: str,
684        options: KnowledgeBaseSearchOptions | None = None,
685    ) -> list[dict]:
686        """Search the knowledge base using semantic search.
687
688        Args:
689            prompt: The search query in natural language.
690            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
691                Supports: ``limit``, ``chunkLimit``, ``rerankProvider``, ``rerankScoreThreshold``,
692                ``chatModel``, ``searchMode``, ``graphOptions``, ``graphFilter``.
693
694        Returns:
695            A list of ``AiKnowledgeBaseSearchResultChunk`` dicts, each with:
696            ``contextId``, ``data``, ``metadata``, ``score``.
697
698        Example::
699
700            chunks = await kb.search(
701                "How do I reset my password?",
702                options={
703                    "limit": 5,
704                    "chatModel": "gemini-3.8-flash",
705                },
706            )
707            for chunk in chunks:
708                print(f"Score: {chunk['score']}, Data: {chunk['data'][:100]}")
709        """
710        response = await self.search_with_graph_context(prompt, options)
711        return response.get("chunks", [])
712
713    # --- Knowledge graph ---
714
715    async def search_with_graph_context(
716        self,
717        prompt: str,
718        options: KnowledgeBaseSearchOptions | None = None,
719    ) -> dict:
720        """Like :meth:`search`, but return the full search response instead of only the chunks.
721
722        With ``searchMode: 'graph'`` and ``graphOptions: {'includeGraphContext': True}`` the
723        response also carries ``graphContext``, the subgraph the search traversed.
724
725        Args:
726            prompt: The search query in natural language.
727            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
728
729        Returns:
730            An ``AiKnowledgeBaseSearchResponse`` dict: ``chunks`` (list of result chunk dicts)
731            and, when requested via ``includeGraphContext``, ``graphContext``
732            (``{'entities': [...], 'relationships': [...]}``).
733        """
734        search_options: dict[str, Any] = {"prompt": prompt, **(options or {})}
735        result = await self._http.post(
736            "squid-api/v1/ai/knowledge-base/search",
737            {
738                "knowledgeBaseId": self._kb_id,
739                "prompt": prompt,
740                "options": search_options,
741            },
742        )
743        return result or {"chunks": []}
744
745    async def get_graph_status(self) -> dict:
746        """Get the knowledge base's graph build/readiness status.
747
748        Returns:
749            A ``GetKnowledgeBaseGraphStatusResponse`` dict with ``enabled``, ``entityCount``,
750            ``relationshipCount``, ``contextsIndexed``, ``contextsTotal``, and, where available,
751            ``facets``, ``topics``, ``structureStale``, LLM usage fields, and ``buildJob``
752            (``{'status': 'in_progress' | 'completed' | 'failed', ...}``) for the most recent
753            rebuild. Poll it after :meth:`rebuild_graph` until ``buildJob`` leaves
754            ``'in_progress'``.
755        """
756        return await self._http.get(f"squid-api/v1/ai/knowledge-base/getGraphStatus/{self._kb_id}")
757
758    async def rebuild_graph(
759        self,
760        *,
761        mode: Literal["full", "structural"] | None = None,
762        ignore_active_bulk_jobs: bool | None = None,
763    ) -> None:
764        """Enqueue a graph rebuild/backfill. Requires ``graphRag.enabled``.
765
766        Only one rebuild can run per knowledge base at a time; track progress via
767        :meth:`get_graph_status`. A rebuild while a bulk-ingestion job on the knowledge base is
768        still active is refused (``GRAPH_REBUILD_BLOCKED_BY_BULK_JOB``).
769
770        Args:
771            mode: ``'structural'`` (the default) keeps the already-extracted entity graph and
772                rebuilds just the concept layer; ``'full'`` wipes the graph and re-extracts every
773                context with an LLM — expensive, and only needed when the extraction itself must
774                be redone (e.g. after changing ``entityTypes`` or ``extractionModel``).
775            ignore_active_bulk_jobs: start the rebuild even while a bulk-ingestion job is active.
776                For a wedged job only: the rebuild re-extracts what a live job is still merging.
777        """
778        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
779        if mode is not None:
780            request["mode"] = mode
781        if ignore_active_bulk_jobs is not None:
782            request["ignoreActiveBulkJobs"] = ignore_active_bulk_jobs
783        await self._http.post("squid-api/v1/ai/knowledge-base/rebuildGraph", request)
784
785    async def query_graph(
786        self,
787        op: KnowledgeBaseGraphQueryOp,
788        *,
789        ref: str | None = None,
790        ref_b: str | None = None,
791        context_id: str | None = None,
792        query: str | None = None,
793        depth: int | None = None,
794        hops: int | None = None,
795        recursive: bool | None = None,
796        limit: int | None = None,
797    ) -> dict:
798        """Query the knowledge base's graph (documents, entities, themes, facets).
799
800        Requires ``graphRag.enabled`` on the knowledge base.
801
802        Args:
803            op: The operation to run.
804            ref: Node ref (name or facet nodeId). Required by resolve/describe/subtree/
805                docsUnder/neighborhood/pathBetween.
806            ref_b: Second node ref for ``pathBetween``.
807            context_id: The document's contextId for ``conceptsOf``.
808            query: The free-text question for ``globalSummary``.
809            depth: ``subtree``: maximum depth below the resolved node. Default (and cap) 3.
810            hops: ``neighborhood``: expansion hops from the resolved entity. Default 1, max 2.
811            recursive: ``docsUnder``: when True (the default), include documents under the whole
812                subtree, not just the node itself.
813            limit: ``docsUnder``: maximum documents returned. Default 25, max 100.
814
815        Returns:
816            A ``QueryKnowledgeBaseGraphResponse`` dict; which keys are present depends on ``op``
817            (e.g. ``overview`` for 'overview', ``matches`` for 'resolve', ``node``/``path``/
818            ``related`` for 'describe', ``tree``, ``documents``/``totalDocs``, ``concepts``,
819            ``graph``, ``pathBetween``, ``summaries``).
820        """
821        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id, "op": op}
822        if ref is not None:
823            request["ref"] = ref
824        if ref_b is not None:
825            request["refB"] = ref_b
826        if context_id is not None:
827            request["contextId"] = context_id
828        if query is not None:
829            request["query"] = query
830        if depth is not None:
831            request["depth"] = depth
832        if hops is not None:
833            request["hops"] = hops
834        if recursive is not None:
835            request["recursive"] = recursive
836        if limit is not None:
837            request["limit"] = limit
838        return await self._http.post("squid-api/v1/ai/knowledge-base/queryGraph", request)
839
840    async def explore_graph(
841        self,
842        *,
843        node_limit: int | None = None,
844        topic_id: str | None = None,
845    ) -> dict:
846        """Get a bounded slice of the knowledge base's entity graph for exploration/visualization.
847
848        Returns the highest-degree entities and the relationships among them. Requires
849        ``graphRag.enabled``.
850
851        Args:
852            node_limit: Hard cap on returned nodes. Default 200, max 1000.
853            topic_id: Restricts the subgraph to entities under the given topic node id. Ids come
854                from :meth:`get_graph_status`'s ``topics`` and churn on rebuild.
855
856        Returns:
857            An ``ExploreKnowledgeBaseGraphResponse`` dict: ``nodes``, ``edges``, and, once the
858            concept layer has been built, ``topics``.
859        """
860        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
861        if node_limit is not None:
862            request["nodeLimit"] = node_limit
863        if topic_id is not None:
864            request["topicId"] = topic_id
865        return await self._http.post("squid-api/v1/ai/knowledge-base/exploreGraph", request)

Operations on a single knowledge base.

Provides methods for managing knowledge base configuration, upserting and searching text/file contexts, and retrieving individual contexts.

Obtained via AiClient.knowledge_base().

Example::

kb = squid.ai().knowledge_base("my-kb")
await kb.upsert(description="Product documentation")
await kb.upsert_contexts(
    [
        {
            "contextId": "doc-1",
            "type": "text",
            "title": "Getting Started",
            "text": "Welcome to our product...",
        }
    ]
)
results = await kb.search("How do I get started?")
KnowledgeBaseClient(http: squidcloud.http.HttpTransport, kb_id: str)
498    def __init__(self, http: HttpTransport, kb_id: str) -> None:
499        self._http = http
500        self._kb_id = kb_id
async def get(self) -> dict | None:
502    async def get(self) -> dict | None:
503        """Get the knowledge base details.
504
505        Returns:
506            An ``AiKnowledgeBase`` dict with keys: ``id``, ``appId``,
507            ``description``, ``metadataFields``, ``embeddingModel``,
508            ``chatModel``, ``updatedAt``. Returns ``None`` if not found.
509        """
510        return await self._http.get(f"squid-api/v1/ai/knowledge-base/get/{self._kb_id}")

Get the knowledge base details.

Returns:

An AiKnowledgeBase dict with keys: id, appId, description, metadataFields, embeddingModel, chatModel, updatedAt. Returns None if not found.

async def upsert( self, *, description: str | None = None, metadata_fields: list[AiKnowledgeBaseMetadataField] | None = None, embedding_model: str | None = None, chat_model: str | IntegrationModelSpec | None = None, name: str | None = None, vector_db_type: Literal['postgres', 'mongoAtlas'] | None = None, graph_rag: AiKnowledgeBaseGraphConfig | None = None) -> None:
512    async def upsert(
513        self,
514        *,
515        description: str | None = None,
516        metadata_fields: list[AiKnowledgeBaseMetadataField] | None = None,
517        embedding_model: str | None = None,
518        chat_model: AiChatModelSelection | None = None,
519        name: str | None = None,
520        vector_db_type: VectorDbType | None = None,
521        graph_rag: AiKnowledgeBaseGraphConfig | None = None,
522    ) -> None:
523        """Create or update the knowledge base.
524
525        Args:
526            description: Description of the knowledge base's content.
527            metadata_fields: Schema for metadata fields used in filtering.
528                Each field: ``{'name': str, 'dataType': str, 'required': bool, 'description'?: str}``.
529            embedding_model: The embedding model name for vectorization.
530                Required when creating a new knowledge base.
531            chat_model: The LLM model for answering questions over this KB.
532            name: Display name for the knowledge base.
533            vector_db_type: The vector store backend. Set at creation (defaulting to the
534                server's default) and immutable thereafter. The knowledge graph requires
535                ``'mongoAtlas'``.
536            graph_rag: Opt-in GraphRAG configuration (``{'enabled': True, ...}``). Only
537                honored on ``'mongoAtlas'`` knowledge bases; mutable, unlike
538                ``vector_db_type``. The value replaces the stored config rather than
539                merging into it. See :class:`AiKnowledgeBaseGraphConfig`.
540        """
541        kb: dict[str, Any] = {"id": self._kb_id}
542        if description is not None:
543            kb["description"] = description
544        if metadata_fields is not None:
545            kb["metadataFields"] = metadata_fields
546        if embedding_model is not None:
547            kb["embeddingModel"] = embedding_model
548        if chat_model is not None:
549            kb["chatModel"] = chat_model
550        if name is not None:
551            kb["name"] = name
552        if vector_db_type is not None:
553            kb["vectorDbType"] = vector_db_type
554        if graph_rag is not None:
555            kb["graphRag"] = graph_rag
556        await self._http.post("squid-api/v1/ai/knowledge-base/upsert", {"knowledgeBase": kb})

Create or update the knowledge base.

Arguments:
  • description: Description of the knowledge base's content.
  • metadata_fields: Schema for metadata fields used in filtering. Each field: {'name': str, 'dataType': str, 'required': bool, 'description'?: str}.
  • embedding_model: The embedding model name for vectorization. Required when creating a new knowledge base.
  • chat_model: The LLM model for answering questions over this KB.
  • name: Display name for the knowledge base.
  • vector_db_type: The vector store backend. Set at creation (defaulting to the server's default) and immutable thereafter. The knowledge graph requires 'mongoAtlas'.
  • graph_rag: Opt-in GraphRAG configuration ({'enabled': True, ...}). Only honored on 'mongoAtlas' knowledge bases; mutable, unlike vector_db_type. The value replaces the stored config rather than merging into it. See AiKnowledgeBaseGraphConfig.
async def delete(self) -> None:
558    async def delete(self) -> None:
559        """Delete the knowledge base and all its contexts permanently."""
560        await self._http.post("squid-api/v1/ai/knowledge-base/delete", {"id": self._kb_id})

Delete the knowledge base and all its contexts permanently.

async def get_context(self, context_id: str) -> dict | None:
564    async def get_context(self, context_id: str) -> dict | None:
565        """Get a specific context entry.
566
567        Args:
568            context_id: The unique context identifier.
569
570        Returns:
571            An ``AiKnowledgeBaseContext`` dict with keys: ``id``, ``appId``,
572            ``knowledgeBaseId``, ``createdAt``, ``updatedAt``, ``type``,
573            ``title``, ``text``, ``preview``, ``sizeBytes``, ``metadata``,
574            ``requestConfig``. Returns ``None`` if not found.
575        """
576        return await self._http.get(
577            f"squid-api/v1/ai/knowledge-base/getContext/{self._kb_id}/{context_id}"
578        )

Get a specific context entry.

Arguments:
  • context_id: The unique context identifier.
Returns:

An AiKnowledgeBaseContext dict with keys: id, appId, knowledgeBaseId, createdAt, updatedAt, type, title, text, preview, sizeBytes, metadata, requestConfig. Returns None if not found.

async def list_contexts(self) -> list[dict]:
580    async def list_contexts(self) -> list[dict]:
581        """List all contexts in the knowledge base.
582
583        Deprecated: fetches every context in one call with no pagination — expensive for large
584        knowledge bases. Use :meth:`list_contexts_page` instead, which supports
585        ``offset``/``limit``/``search``.
586
587        Returns:
588            A list of ``AiKnowledgeBaseContext`` dicts.
589        """
590        result = await self._http.get(f"squid-api/v1/ai/knowledge-base/listContexts/{self._kb_id}")
591        return result.get("contexts", []) if result else []

List all contexts in the knowledge base.

Deprecated: fetches every context in one call with no pagination — expensive for large knowledge bases. Use list_contexts_page() instead, which supports offset/limit/search.

Returns:

A list of AiKnowledgeBaseContext dicts.

async def list_contexts_page( self, *, offset: int | None = None, limit: int | None = None, search: str | None = None) -> dict:
593    async def list_contexts_page(
594        self,
595        *,
596        offset: int | None = None,
597        limit: int | None = None,
598        search: str | None = None,
599    ) -> dict:
600        """List a page of contexts in the knowledge base.
601
602        Args:
603            offset: The number of contexts to skip, for pagination.
604            limit: The maximum number of contexts to return, for pagination.
605            search: Case-insensitive substring search across id/title only.
606
607        Returns:
608            A dict with ``contexts`` (a list of ``AiKnowledgeBaseContext`` dicts for the
609            requested page) and ``totalCount`` (the total number of contexts in the
610            knowledge base, ignoring ``offset``/``limit``).
611        """
612        params = {
613            key: str(value)
614            for key, value in {"offset": offset, "limit": limit, "search": search}.items()
615            if value is not None
616        }
617        result = await self._http.get(
618            f"squid-api/v1/ai/knowledge-base/listContextsPage/{self._kb_id}",
619            params=params or None,
620        )
621        return result if result else {"contexts": [], "totalCount": 0}

List a page of contexts in the knowledge base.

Arguments:
  • offset: The number of contexts to skip, for pagination.
  • limit: The maximum number of contexts to return, for pagination.
  • search: Case-insensitive substring search across id/title only.
Returns:

A dict with contexts (a list of AiKnowledgeBaseContext dicts for the requested page) and totalCount (the total number of contexts in the knowledge base, ignoring offset/limit).

async def upsert_contexts( self, contexts: list[TextContextRequest | FileContextRequest], files: list[tuple[str, bytes, str]] | None = None) -> dict:
623    async def upsert_contexts(
624        self,
625        contexts: list[ContextRequest],
626        files: list[tuple[str, bytes, str]] | None = None,
627    ) -> dict:
628        """Add or update contexts in the knowledge base.
629
630        Args:
631            contexts: A list of context request objects. Each must be either:
632                - A :class:`TextContextRequest`: ``{'contextId', 'type': 'text', 'title', 'text', ...}``
633                - A :class:`FileContextRequest`: ``{'contextId', 'type': 'file', ...}``
634            files: For file contexts, provide the actual file data as a list of
635                ``(filename, data_bytes, content_type)`` tuples. Must match the
636                order of file contexts in the ``contexts`` list.
637
638        Returns:
639            A dict with ``failures``: a list of ``UpsertContextStatusError`` dicts
640            for any contexts that failed to upsert.
641
642        Example::
643
644            await kb.upsert_contexts(
645                [
646                    {"contextId": "doc-1", "type": "text", "title": "FAQ", "text": "..."},
647                    {"contextId": "doc-2", "type": "file"},
648                ],
649                files=[
650                    ("manual.pdf", pdf_bytes, "application/pdf"),
651                ],
652            )
653        """
654        form_data = {
655            "knowledgeBaseId": self._kb_id,
656            "contexts": json.dumps(contexts),
657        }
658        file_tuples: list[tuple[str, tuple[str, bytes, str]]] = []
659        if files:
660            for fname, fdata, ftype in files:
661                file_tuples.append(("files", (fname, fdata, ftype)))
662        return await self._http.post_form(
663            "squid-api/v1/ai/knowledge-base/upsertContexts",
664            data=form_data,
665            files=file_tuples,
666        )

Add or update contexts in the knowledge base.

Arguments:
  • contexts: A list of context request objects. Each must be either:
  • files: For file contexts, provide the actual file data as a list of (filename, data_bytes, content_type) tuples. Must match the order of file contexts in the contexts list.
Returns:

A dict with failures: a list of UpsertContextStatusError dicts for any contexts that failed to upsert.

Example::

await kb.upsert_contexts(
    [
        {"contextId": "doc-1", "type": "text", "title": "FAQ", "text": "..."},
        {"contextId": "doc-2", "type": "file"},
    ],
    files=[
        ("manual.pdf", pdf_bytes, "application/pdf"),
    ],
)
async def delete_contexts(self, context_ids: list[str]) -> None:
668    async def delete_contexts(self, context_ids: list[str]) -> None:
669        """Delete contexts by their IDs.
670
671        Args:
672            context_ids: List of context IDs to delete.
673        """
674        await self._http.post(
675            "squid-api/v1/ai/knowledge-base/deleteContexts",
676            {"knowledgeBaseId": self._kb_id, "contextIds": context_ids},
677        )

Delete contexts by their IDs.

Arguments:
  • context_ids: List of context IDs to delete.
async def search( self, prompt: str, options: KnowledgeBaseSearchOptions | None = None) -> list[dict]:
681    async def search(
682        self,
683        prompt: str,
684        options: KnowledgeBaseSearchOptions | None = None,
685    ) -> list[dict]:
686        """Search the knowledge base using semantic search.
687
688        Args:
689            prompt: The search query in natural language.
690            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
691                Supports: ``limit``, ``chunkLimit``, ``rerankProvider``, ``rerankScoreThreshold``,
692                ``chatModel``, ``searchMode``, ``graphOptions``, ``graphFilter``.
693
694        Returns:
695            A list of ``AiKnowledgeBaseSearchResultChunk`` dicts, each with:
696            ``contextId``, ``data``, ``metadata``, ``score``.
697
698        Example::
699
700            chunks = await kb.search(
701                "How do I reset my password?",
702                options={
703                    "limit": 5,
704                    "chatModel": "gemini-3.8-flash",
705                },
706            )
707            for chunk in chunks:
708                print(f"Score: {chunk['score']}, Data: {chunk['data'][:100]}")
709        """
710        response = await self.search_with_graph_context(prompt, options)
711        return response.get("chunks", [])

Search the knowledge base using semantic search.

Arguments:
  • prompt: The search query in natural language.
  • options: Search options. See KnowledgeBaseSearchOptions. Supports: limit, chunkLimit, rerankProvider, rerankScoreThreshold, chatModel, searchMode, graphOptions, graphFilter.
Returns:

A list of AiKnowledgeBaseSearchResultChunk dicts, each with: contextId, data, metadata, score.

Example::

chunks = await kb.search(
    "How do I reset my password?",
    options={
        "limit": 5,
        "chatModel": "gemini-3.8-flash",
    },
)
for chunk in chunks:
    print(f"Score: {chunk['score']}, Data: {chunk['data'][:100]}")
async def search_with_graph_context( self, prompt: str, options: KnowledgeBaseSearchOptions | None = None) -> dict:
715    async def search_with_graph_context(
716        self,
717        prompt: str,
718        options: KnowledgeBaseSearchOptions | None = None,
719    ) -> dict:
720        """Like :meth:`search`, but return the full search response instead of only the chunks.
721
722        With ``searchMode: 'graph'`` and ``graphOptions: {'includeGraphContext': True}`` the
723        response also carries ``graphContext``, the subgraph the search traversed.
724
725        Args:
726            prompt: The search query in natural language.
727            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
728
729        Returns:
730            An ``AiKnowledgeBaseSearchResponse`` dict: ``chunks`` (list of result chunk dicts)
731            and, when requested via ``includeGraphContext``, ``graphContext``
732            (``{'entities': [...], 'relationships': [...]}``).
733        """
734        search_options: dict[str, Any] = {"prompt": prompt, **(options or {})}
735        result = await self._http.post(
736            "squid-api/v1/ai/knowledge-base/search",
737            {
738                "knowledgeBaseId": self._kb_id,
739                "prompt": prompt,
740                "options": search_options,
741            },
742        )
743        return result or {"chunks": []}

Like search(), but return the full search response instead of only the chunks.

With searchMode: 'graph' and graphOptions: {'includeGraphContext': True} the response also carries graphContext, the subgraph the search traversed.

Arguments:
Returns:

An AiKnowledgeBaseSearchResponse dict: chunks (list of result chunk dicts) and, when requested via includeGraphContext, graphContext ({'entities': [...], 'relationships': [...]}).

async def get_graph_status(self) -> dict:
745    async def get_graph_status(self) -> dict:
746        """Get the knowledge base's graph build/readiness status.
747
748        Returns:
749            A ``GetKnowledgeBaseGraphStatusResponse`` dict with ``enabled``, ``entityCount``,
750            ``relationshipCount``, ``contextsIndexed``, ``contextsTotal``, and, where available,
751            ``facets``, ``topics``, ``structureStale``, LLM usage fields, and ``buildJob``
752            (``{'status': 'in_progress' | 'completed' | 'failed', ...}``) for the most recent
753            rebuild. Poll it after :meth:`rebuild_graph` until ``buildJob`` leaves
754            ``'in_progress'``.
755        """
756        return await self._http.get(f"squid-api/v1/ai/knowledge-base/getGraphStatus/{self._kb_id}")

Get the knowledge base's graph build/readiness status.

Returns:

A GetKnowledgeBaseGraphStatusResponse dict with enabled, entityCount, relationshipCount, contextsIndexed, contextsTotal, and, where available, facets, topics, structureStale, LLM usage fields, and buildJob ({'status': 'in_progress' | 'completed' | 'failed', ...}) for the most recent rebuild. Poll it after rebuild_graph() until buildJob leaves 'in_progress'.

async def rebuild_graph( self, *, mode: Literal['full', 'structural'] | None = None, ignore_active_bulk_jobs: bool | None = None) -> None:
758    async def rebuild_graph(
759        self,
760        *,
761        mode: Literal["full", "structural"] | None = None,
762        ignore_active_bulk_jobs: bool | None = None,
763    ) -> None:
764        """Enqueue a graph rebuild/backfill. Requires ``graphRag.enabled``.
765
766        Only one rebuild can run per knowledge base at a time; track progress via
767        :meth:`get_graph_status`. A rebuild while a bulk-ingestion job on the knowledge base is
768        still active is refused (``GRAPH_REBUILD_BLOCKED_BY_BULK_JOB``).
769
770        Args:
771            mode: ``'structural'`` (the default) keeps the already-extracted entity graph and
772                rebuilds just the concept layer; ``'full'`` wipes the graph and re-extracts every
773                context with an LLM — expensive, and only needed when the extraction itself must
774                be redone (e.g. after changing ``entityTypes`` or ``extractionModel``).
775            ignore_active_bulk_jobs: start the rebuild even while a bulk-ingestion job is active.
776                For a wedged job only: the rebuild re-extracts what a live job is still merging.
777        """
778        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
779        if mode is not None:
780            request["mode"] = mode
781        if ignore_active_bulk_jobs is not None:
782            request["ignoreActiveBulkJobs"] = ignore_active_bulk_jobs
783        await self._http.post("squid-api/v1/ai/knowledge-base/rebuildGraph", request)

Enqueue a graph rebuild/backfill. Requires graphRag.enabled.

Only one rebuild can run per knowledge base at a time; track progress via get_graph_status(). A rebuild while a bulk-ingestion job on the knowledge base is still active is refused (GRAPH_REBUILD_BLOCKED_BY_BULK_JOB).

Arguments:
  • mode: 'structural' (the default) keeps the already-extracted entity graph and rebuilds just the concept layer; 'full' wipes the graph and re-extracts every context with an LLM — expensive, and only needed when the extraction itself must be redone (e.g. after changing entityTypes or extractionModel).
  • ignore_active_bulk_jobs: start the rebuild even while a bulk-ingestion job is active. For a wedged job only: the rebuild re-extracts what a live job is still merging.
async def query_graph( self, op: Literal['overview', 'resolve', 'describe', 'subtree', 'docsUnder', 'conceptsOf', 'neighborhood', 'pathBetween', 'globalSummary'], *, ref: str | None = None, ref_b: str | None = None, context_id: str | None = None, query: str | None = None, depth: int | None = None, hops: int | None = None, recursive: bool | None = None, limit: int | None = None) -> dict:
785    async def query_graph(
786        self,
787        op: KnowledgeBaseGraphQueryOp,
788        *,
789        ref: str | None = None,
790        ref_b: str | None = None,
791        context_id: str | None = None,
792        query: str | None = None,
793        depth: int | None = None,
794        hops: int | None = None,
795        recursive: bool | None = None,
796        limit: int | None = None,
797    ) -> dict:
798        """Query the knowledge base's graph (documents, entities, themes, facets).
799
800        Requires ``graphRag.enabled`` on the knowledge base.
801
802        Args:
803            op: The operation to run.
804            ref: Node ref (name or facet nodeId). Required by resolve/describe/subtree/
805                docsUnder/neighborhood/pathBetween.
806            ref_b: Second node ref for ``pathBetween``.
807            context_id: The document's contextId for ``conceptsOf``.
808            query: The free-text question for ``globalSummary``.
809            depth: ``subtree``: maximum depth below the resolved node. Default (and cap) 3.
810            hops: ``neighborhood``: expansion hops from the resolved entity. Default 1, max 2.
811            recursive: ``docsUnder``: when True (the default), include documents under the whole
812                subtree, not just the node itself.
813            limit: ``docsUnder``: maximum documents returned. Default 25, max 100.
814
815        Returns:
816            A ``QueryKnowledgeBaseGraphResponse`` dict; which keys are present depends on ``op``
817            (e.g. ``overview`` for 'overview', ``matches`` for 'resolve', ``node``/``path``/
818            ``related`` for 'describe', ``tree``, ``documents``/``totalDocs``, ``concepts``,
819            ``graph``, ``pathBetween``, ``summaries``).
820        """
821        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id, "op": op}
822        if ref is not None:
823            request["ref"] = ref
824        if ref_b is not None:
825            request["refB"] = ref_b
826        if context_id is not None:
827            request["contextId"] = context_id
828        if query is not None:
829            request["query"] = query
830        if depth is not None:
831            request["depth"] = depth
832        if hops is not None:
833            request["hops"] = hops
834        if recursive is not None:
835            request["recursive"] = recursive
836        if limit is not None:
837            request["limit"] = limit
838        return await self._http.post("squid-api/v1/ai/knowledge-base/queryGraph", request)

Query the knowledge base's graph (documents, entities, themes, facets).

Requires graphRag.enabled on the knowledge base.

Arguments:
  • op: The operation to run.
  • ref: Node ref (name or facet nodeId). Required by resolve/describe/subtree/ docsUnder/neighborhood/pathBetween.
  • ref_b: Second node ref for pathBetween.
  • context_id: The document's contextId for conceptsOf.
  • query: The free-text question for globalSummary.
  • depth: subtree: maximum depth below the resolved node. Default (and cap) 3.
  • hops: neighborhood: expansion hops from the resolved entity. Default 1, max 2.
  • recursive: docsUnder: when True (the default), include documents under the whole subtree, not just the node itself.
  • limit: docsUnder: maximum documents returned. Default 25, max 100.
Returns:

A QueryKnowledgeBaseGraphResponse dict; which keys are present depends on op (e.g. overview for 'overview', matches for 'resolve', node/path/ related for 'describe', tree, documents/totalDocs, concepts, graph, pathBetween, summaries).

async def explore_graph( self, *, node_limit: int | None = None, topic_id: str | None = None) -> dict:
840    async def explore_graph(
841        self,
842        *,
843        node_limit: int | None = None,
844        topic_id: str | None = None,
845    ) -> dict:
846        """Get a bounded slice of the knowledge base's entity graph for exploration/visualization.
847
848        Returns the highest-degree entities and the relationships among them. Requires
849        ``graphRag.enabled``.
850
851        Args:
852            node_limit: Hard cap on returned nodes. Default 200, max 1000.
853            topic_id: Restricts the subgraph to entities under the given topic node id. Ids come
854                from :meth:`get_graph_status`'s ``topics`` and churn on rebuild.
855
856        Returns:
857            An ``ExploreKnowledgeBaseGraphResponse`` dict: ``nodes``, ``edges``, and, once the
858            concept layer has been built, ``topics``.
859        """
860        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
861        if node_limit is not None:
862            request["nodeLimit"] = node_limit
863        if topic_id is not None:
864            request["topicId"] = topic_id
865        return await self._http.post("squid-api/v1/ai/knowledge-base/exploreGraph", request)

Get a bounded slice of the knowledge base's entity graph for exploration/visualization.

Returns the highest-degree entities and the relationships among them. Requires graphRag.enabled.

Arguments:
  • node_limit: Hard cap on returned nodes. Default 200, max 1000.
  • topic_id: Restricts the subgraph to entities under the given topic node id. Ids come from get_graph_status()'s topics and churn on rebuild.
Returns:

An ExploreKnowledgeBaseGraphResponse dict: nodes, edges, and, once the concept layer has been built, topics.

KnowledgeBaseGraphQueryOp = typing.Literal['overview', 'resolve', 'describe', 'subtree', 'docsUnder', 'conceptsOf', 'neighborhood', 'pathBetween', 'globalSummary']
class KnowledgeBaseSearchOptions(typing.TypedDict):
678class KnowledgeBaseSearchOptions(TypedDict, total=False):
679    """Options for knowledge base search."""
680
681    prompt: str
682    """The search prompt."""
683    limit: int
684    """Max number of results to return."""
685    chunkLimit: int
686    """How many chunks to search over (default 100)."""
687    rerankProvider: AiRerankProvider
688    """Reranker provider (default 'cohere')."""
689    rerankScoreThreshold: float
690    """Minimum reranker relevance score a chunk must reach to stay once at least 10 chunks are
691    already kept (default 0.2). Score ranges differ per reranker."""
692    chatModel: AiChatModelSelection
693    """Model to use for answering."""
694    searchMode: Literal["vector", "hybrid", "keyword", "graph"]
695    """Retrieval mode: 'hybrid' (BM25+dense fusion where supported; default on non-graph KBs),
696    'vector' (dense only), 'keyword' (pure lexical — every whitespace-separated term must appear
697    as a literal, case-insensitive substring), or 'graph' (GraphRAG — seeds entities via vector
698    search, expands relationships via graph traversal, maps entities back to their chunks, fused
699    with a hybrid search; requires a ``vectorDbType: 'mongoAtlas'`` KB with ``graphRag.enabled``
700    and is the default there)."""
701    graphOptions: AiKnowledgeBaseGraphSearchOptions
702    """Tuning for ``searchMode: 'graph'``. Ignored for other search modes."""
703    graphFilter: AiKnowledgeBaseGraphFilter
704    """Scopes the search to the documents under one concept of the KB's graph. Requires
705    ``graphRag.enabled``."""

Options for knowledge base search.

prompt: str

The search prompt.

limit: int

Max number of results to return.

chunkLimit: int

How many chunks to search over (default 100).

rerankProvider: Literal['cohere', 'voyage', 'none']

Reranker provider (default 'cohere').

rerankScoreThreshold: float

Minimum reranker relevance score a chunk must reach to stay once at least 10 chunks are already kept (default 0.2). Score ranges differ per reranker.

chatModel: str | IntegrationModelSpec

Model to use for answering.

searchMode: Literal['vector', 'hybrid', 'keyword', 'graph']

Retrieval mode: 'hybrid' (BM25+dense fusion where supported; default on non-graph KBs), 'vector' (dense only), 'keyword' (pure lexical — every whitespace-separated term must appear as a literal, case-insensitive substring), or 'graph' (GraphRAG — seeds entities via vector search, expands relationships via graph traversal, maps entities back to their chunks, fused with a hybrid search; requires a vectorDbType: 'mongoAtlas' KB with graphRag.enabled and is the default there).

Tuning for searchMode: 'graph'. Ignored for other search modes.

Scopes the search to the documents under one concept of the KB's graph. Requires graphRag.enabled.

class MatchmakingClient:
 21class MatchmakingClient:
 22    """AI-powered matchmaking: match makers, entities, matching."""
 23
 24    def __init__(self, http: HttpTransport) -> None:
 25        self._http = http
 26
 27    # --- Match makers ---
 28
 29    async def create_match_maker(
 30        self,
 31        match_maker_id: str,
 32        description: str,
 33        categories: list[MmCategory],
 34    ) -> dict:
 35        """Create a new match maker."""
 36        return await self._http.post(
 37            "squid-api/v1/ai/matchmaking/createMatchMaker",
 38            {"id": match_maker_id, "description": description, "categories": categories},
 39        )
 40
 41    async def get_match_maker(self, match_maker_id: str) -> dict | None:
 42        """Get a match maker by ID."""
 43        result = await self._http.get(f"squid-api/v1/ai/matchmaking/getMatchMaker/{match_maker_id}")
 44        return result.get("matchMaker") if result else None
 45
 46    async def list_match_makers(self) -> list[dict]:
 47        """List all match makers."""
 48        result = await self._http.get("squid-api/v1/ai/matchmaking/listMatchMakers")
 49        return result.get("matchMakers", []) if result else []
 50
 51    async def delete_match_maker(self, match_maker_id: str) -> None:
 52        """Delete a match maker."""
 53        await self._http.post(
 54            "squid-api/v1/ai/matchmaking/deleteMatchMaker",
 55            {"matchMakerId": match_maker_id},
 56        )
 57
 58    # --- Entities ---
 59
 60    async def insert_entities(self, match_maker_id: str, entities: list[MmEntity]) -> None:
 61        """Insert entities into a match maker."""
 62        await self._http.post(
 63            "squid-api/v1/ai/matchmaking/insertEntities",
 64            {"matchMakerId": match_maker_id, "entities": entities},
 65        )
 66
 67    async def delete_entity(self, match_maker_id: str, entity_id: str) -> None:
 68        """Delete an entity."""
 69        await self._http.post(
 70            "squid-api/v1/ai/matchmaking/deleteEntity",
 71            {"matchMakerId": match_maker_id, "entityId": entity_id},
 72        )
 73
 74    async def get_entity(self, match_maker_id: str, entity_id: str) -> dict | None:
 75        """Get a specific entity."""
 76        result = await self._http.get(
 77            f"squid-api/v1/ai/matchmaking/getEntity/{match_maker_id}/{entity_id}"
 78        )
 79        return result.get("entity") if result else None
 80
 81    async def list_entities(
 82        self,
 83        match_maker_id: str,
 84        category_id: str,
 85        options: MmListEntitiesOptions | None = None,
 86    ) -> list[dict]:
 87        """List entities in a category."""
 88        result = await self._http.post(
 89            "squid-api/v1/ai/matchmaking/listEntities",
 90            {
 91                "matchMakerId": match_maker_id,
 92                "categoryId": category_id,
 93                "options": options or {},
 94            },
 95        )
 96        return result.get("entities", []) if result else []
 97
 98    # --- Matching ---
 99
100    async def find_matches(
101        self,
102        match_maker_id: str,
103        entity_id: str,
104        options: MmFindMatchesOptions | None = None,
105    ) -> list[dict]:
106        """Find matches for an existing entity.
107
108        Returns list of MmEntityMatch dicts with:
109        id, content, categoryId, metadata, score, reasoning.
110        """
111        result = await self._http.post(
112            "squid-api/v1/ai/matchmaking/findMatches",
113            {
114                "matchMakerId": match_maker_id,
115                "entityId": entity_id,
116                "options": options or {},
117            },
118        )
119        return result.get("matches", []) if result else []
120
121    async def find_matches_for_entity(
122        self,
123        match_maker_id: str,
124        entity: MmEntity,
125        options: MmFindMatchesOptions | None = None,
126    ) -> list[dict]:
127        """Find matches for a new entity (not yet inserted)."""
128        result = await self._http.post(
129            "squid-api/v1/ai/matchmaking/findMatchesForEntity",
130            {
131                "matchMakerId": match_maker_id,
132                "entity": entity,
133                "options": options or {},
134            },
135        )
136        return result.get("matches", []) if result else []

AI-powered matchmaking: match makers, entities, matching.

MatchmakingClient(http: squidcloud.http.HttpTransport)
24    def __init__(self, http: HttpTransport) -> None:
25        self._http = http
async def create_match_maker( self, match_maker_id: str, description: str, categories: list[MmCategory]) -> dict:
29    async def create_match_maker(
30        self,
31        match_maker_id: str,
32        description: str,
33        categories: list[MmCategory],
34    ) -> dict:
35        """Create a new match maker."""
36        return await self._http.post(
37            "squid-api/v1/ai/matchmaking/createMatchMaker",
38            {"id": match_maker_id, "description": description, "categories": categories},
39        )

Create a new match maker.

async def get_match_maker(self, match_maker_id: str) -> dict | None:
41    async def get_match_maker(self, match_maker_id: str) -> dict | None:
42        """Get a match maker by ID."""
43        result = await self._http.get(f"squid-api/v1/ai/matchmaking/getMatchMaker/{match_maker_id}")
44        return result.get("matchMaker") if result else None

Get a match maker by ID.

async def list_match_makers(self) -> list[dict]:
46    async def list_match_makers(self) -> list[dict]:
47        """List all match makers."""
48        result = await self._http.get("squid-api/v1/ai/matchmaking/listMatchMakers")
49        return result.get("matchMakers", []) if result else []

List all match makers.

async def delete_match_maker(self, match_maker_id: str) -> None:
51    async def delete_match_maker(self, match_maker_id: str) -> None:
52        """Delete a match maker."""
53        await self._http.post(
54            "squid-api/v1/ai/matchmaking/deleteMatchMaker",
55            {"matchMakerId": match_maker_id},
56        )

Delete a match maker.

async def insert_entities( self, match_maker_id: str, entities: list[MmEntity]) -> None:
60    async def insert_entities(self, match_maker_id: str, entities: list[MmEntity]) -> None:
61        """Insert entities into a match maker."""
62        await self._http.post(
63            "squid-api/v1/ai/matchmaking/insertEntities",
64            {"matchMakerId": match_maker_id, "entities": entities},
65        )

Insert entities into a match maker.

async def delete_entity(self, match_maker_id: str, entity_id: str) -> None:
67    async def delete_entity(self, match_maker_id: str, entity_id: str) -> None:
68        """Delete an entity."""
69        await self._http.post(
70            "squid-api/v1/ai/matchmaking/deleteEntity",
71            {"matchMakerId": match_maker_id, "entityId": entity_id},
72        )

Delete an entity.

async def get_entity(self, match_maker_id: str, entity_id: str) -> dict | None:
74    async def get_entity(self, match_maker_id: str, entity_id: str) -> dict | None:
75        """Get a specific entity."""
76        result = await self._http.get(
77            f"squid-api/v1/ai/matchmaking/getEntity/{match_maker_id}/{entity_id}"
78        )
79        return result.get("entity") if result else None

Get a specific entity.

async def list_entities( self, match_maker_id: str, category_id: str, options: MmListEntitiesOptions | None = None) -> list[dict]:
81    async def list_entities(
82        self,
83        match_maker_id: str,
84        category_id: str,
85        options: MmListEntitiesOptions | None = None,
86    ) -> list[dict]:
87        """List entities in a category."""
88        result = await self._http.post(
89            "squid-api/v1/ai/matchmaking/listEntities",
90            {
91                "matchMakerId": match_maker_id,
92                "categoryId": category_id,
93                "options": options or {},
94            },
95        )
96        return result.get("entities", []) if result else []

List entities in a category.

async def find_matches( self, match_maker_id: str, entity_id: str, options: MmFindMatchesOptions | None = None) -> list[dict]:
100    async def find_matches(
101        self,
102        match_maker_id: str,
103        entity_id: str,
104        options: MmFindMatchesOptions | None = None,
105    ) -> list[dict]:
106        """Find matches for an existing entity.
107
108        Returns list of MmEntityMatch dicts with:
109        id, content, categoryId, metadata, score, reasoning.
110        """
111        result = await self._http.post(
112            "squid-api/v1/ai/matchmaking/findMatches",
113            {
114                "matchMakerId": match_maker_id,
115                "entityId": entity_id,
116                "options": options or {},
117            },
118        )
119        return result.get("matches", []) if result else []

Find matches for an existing entity.

Returns list of MmEntityMatch dicts with: id, content, categoryId, metadata, score, reasoning.

async def find_matches_for_entity( self, match_maker_id: str, entity: MmEntity, options: MmFindMatchesOptions | None = None) -> list[dict]:
121    async def find_matches_for_entity(
122        self,
123        match_maker_id: str,
124        entity: MmEntity,
125        options: MmFindMatchesOptions | None = None,
126    ) -> list[dict]:
127        """Find matches for a new entity (not yet inserted)."""
128        result = await self._http.post(
129            "squid-api/v1/ai/matchmaking/findMatchesForEntity",
130            {
131                "matchMakerId": match_maker_id,
132                "entity": entity,
133                "options": options or {},
134            },
135        )
136        return result.get("matches", []) if result else []

Find matches for a new entity (not yet inserted).

class MmCategory(typing.TypedDict):
811class MmCategory(TypedDict):
812    """Matchmaking category."""
813
814    id: str
815    description: str

Matchmaking category.

id: str
description: str
class MmEntity(typing.TypedDict):
818class MmEntity(TypedDict, total=False):
819    """Matchmaking entity."""
820
821    id: str
822    content: str
823    categoryId: str
824    metadata: dict[str, Any]

Matchmaking entity.

id: str
content: str
categoryId: str
metadata: dict[str, typing.Any]
class MmFindMatchesOptions(typing.TypedDict):
827class MmFindMatchesOptions(TypedDict, total=False):
828    """Options for finding matches."""
829
830    metadataFilter: dict[str, Any]
831    """AiContextMetadataFilter conditions."""
832    limit: int
833    """Max matches to return (default 100, max 100)."""
834    matchToCategoryId: str
835    """Category to match against."""

Options for finding matches.

metadataFilter: dict[str, typing.Any]

AiContextMetadataFilter conditions.

limit: int

Max matches to return (default 100, max 100).

matchToCategoryId: str

Category to match against.

class MmListEntitiesOptions(typing.TypedDict):
838class MmListEntitiesOptions(TypedDict, total=False):
839    """Options for listing entities."""
840
841    metadataFilter: dict[str, Any]
842    limit: int

Options for listing entities.

metadataFilter: dict[str, typing.Any]
limit: int
class ModelIdSpec(typing.TypedDict):
45class ModelIdSpec(TypedDict, total=False):
46    """An AI chat model available to the app.
47
48    Returned by :meth:`AiClient.list_chat_models`.
49    """
50
51    modelId: AiChatModelName
52    """The model ID used for API calls. Required."""
53    integrationId: str
54    """The integration ID if this model comes from an integration (OpenAI-compatible,
55    Bedrock). Absent for Squid-provided models."""
56    displayName: str
57    """Human-readable display name for the model (e.g. 'GPT-4o')."""
58    description: str
59    """Short human-readable description of the model. Absent for custom integration models."""
60    replacedBy: AiChatModelName
61    """Set only for deprecated models: the active model that calls to this model are
62    routed to. Absent for active models."""
63    source: AiModelSource
64    """Where the model comes from: 'vendor' (Squid-provided), 'connector' (provided by an
65    integration configured on the app), or 'custom' (user-added model from the app's bundle)."""

An AI chat model available to the app.

Returned by AiClient.list_chat_models().

modelId: str

The model ID used for API calls. Required.

integrationId: str

The integration ID if this model comes from an integration (OpenAI-compatible, Bedrock). Absent for Squid-provided models.

displayName: str

Human-readable display name for the model (e.g. 'GPT-4o').

description: str

Short human-readable description of the model. Absent for custom integration models.

replacedBy: str

Set only for deprecated models: the active model that calls to this model are routed to. Absent for active models.

source: Literal['vendor', 'connector', 'custom']

Where the model comes from: 'vendor' (Squid-provided), 'connector' (provided by an integration configured on the app), or 'custom' (user-added model from the app's bundle).

class Squid:
 24class Squid:
 25    """Squid Cloud Python client.
 26
 27    The main entry point for interacting with the Squid Cloud platform.
 28    Provides access to AI agents, knowledge bases, web utilities,
 29    matchmaking, document extraction, backend functions, and webhooks.
 30
 31    Usage::
 32
 33        squid = Squid(
 34            app_id="my-app",
 35            api_key="sk-...",
 36            region="us-east-1.aws",
 37            environment_id="dev",
 38        )
 39
 40        # AI agent
 41        response = await squid.ai().agent("my-agent").ask("Hello!")
 42
 43        # Execute backend function
 44        result = await squid.execute_function("MyService:greet", "World")
 45
 46        # Web search
 47        results = await squid.web().ai_search("latest news")
 48
 49        # Cleanup
 50        await squid.close()
 51
 52    Can also be used as an async context manager::
 53
 54        async with Squid(app_id="my-app", api_key="sk-...", region="us-east-1.aws") as squid:
 55            result = await squid.ai().agent("my-agent").ask("Hello!")
 56    """
 57
 58    def __init__(
 59        self,
 60        app_id: str,
 61        region: str,
 62        api_key: str | None = None,
 63        environment_id: str | None = None,
 64        squid_developer_id: str | None = None,
 65    ) -> None:
 66        """Initialize the Squid client.
 67
 68        Args:
 69            app_id: The Squid application ID.
 70            region: The deployment region (e.g., 'us-east-1.aws', 'local').
 71            api_key: API key for authentication. Required for most operations.
 72            environment_id: Environment identifier (e.g., 'dev', 'prod').
 73                Appended to app_id as '{app_id}-{environment_id}'.
 74            squid_developer_id: Developer identifier for local development.
 75                Appended to app_id as '{app_id}-{environment_id}-{developer_id}'.
 76        """
 77        self._app_id = app_id
 78        self._region = region
 79        self._environment_id = environment_id
 80        self._squid_developer_id = squid_developer_id
 81
 82        full_app_id = app_id
 83        if environment_id:
 84            full_app_id = f"{app_id}-{environment_id}"
 85        if squid_developer_id:
 86            full_app_id = f"{full_app_id}-{squid_developer_id}"
 87
 88        self._full_app_id = full_app_id
 89        self._http = HttpTransport(
 90            app_id=full_app_id,
 91            region=region,
 92            api_key=api_key,
 93        )
 94
 95    @property
 96    def app_id(self) -> str:
 97        """The application ID (without environment/developer suffix)."""
 98        return self._app_id
 99
100    @property
101    def region(self) -> str:
102        """The deployment region."""
103        return self._region
104
105    @property
106    def client_id(self) -> str:
107        """The unique client instance ID (UUID generated per Squid instance)."""
108        return self._http.client_id
109
110    # --- Sub-clients ---
111
112    def ai(self) -> AiClient:
113        """Access AI operations.
114
115        Provides access to AI agents, knowledge bases, image generation,
116        and audio transcription/synthesis.
117
118        Returns:
119            An :class:`AiClient` instance.
120
121        Example::
122
123            agent = squid.ai().agent("my-agent")
124            response = await agent.ask("What is the weather?")
125
126            kb = squid.ai().knowledge_base("my-kb")
127            results = await kb.search("query")
128        """
129        return AiClient(self._http)
130
131    def web(self) -> WebClient:
132        """Access web utilities.
133
134        Provides AI-powered web search, URL content extraction,
135        and short URL management.
136
137        Returns:
138            A :class:`WebClient` instance.
139
140        Example::
141
142            content = await squid.web().get_url_content("https://example.com")
143            results = await squid.web().ai_search("latest AI news")
144        """
145        return WebClient(self._http)
146
147    def matchmaking(self) -> MatchmakingClient:
148        """Access AI-powered matchmaking.
149
150        Provides match maker management, entity CRUD,
151        and AI-powered entity matching.
152
153        Returns:
154            A :class:`MatchmakingClient` instance.
155
156        Example::
157
158            mm = squid.matchmaking()
159            await mm.create_match_maker("jobs", "Job matching", categories=[...])
160            matches = await mm.find_matches("jobs", "entity-1")
161        """
162        return MatchmakingClient(self._http)
163
164    def extraction(self) -> ExtractionClient:
165        """Access document extraction and PDF creation.
166
167        Provides PDF generation from HTML or URLs, and structured data
168        extraction from documents (PDF, DOCX, images, etc.).
169
170        Returns:
171            An :class:`ExtractionClient` instance.
172
173        Example::
174
175            pdf = await squid.extraction().create_pdf_from_html("<h1>Hello</h1>")
176            data = await squid.extraction().extract_data_from_document_url(
177                "https://example.com/doc.pdf"
178            )
179        """
180        return ExtractionClient(self._http)
181
182    # --- Execute backend function ---
183
184    async def execute_function(
185        self,
186        function_name: str,
187        *params: Any,
188    ) -> Any:
189        """Execute a backend function by name.
190
191        Calls a function defined in a Squid backend service using the
192        ``@executable()`` decorator.
193
194        Args:
195            function_name: The service function name in the format
196                ``"ClassName:methodName"`` (e.g., ``"MyService:greet"``).
197            *params: Positional arguments to pass to the function.
198                Must be JSON-serializable.
199
200        Returns:
201            The function's return value, deserialized from JSON.
202
203        Raises:
204            SquidHttpError: If the function is not found or throws an error.
205
206        Example::
207
208            result = await squid.execute_function("MyService:greet", "World")
209            # result == "Hello, World!"
210
211            data = await squid.execute_function("MyService:getData", 42, True)
212        """
213        result = await self._http.post(
214            f"backend-function/execute?{function_name}",
215            {
216                "functionName": function_name,
217                "paramsArrayStr": json.dumps(list(params)),
218            },
219        )
220        if isinstance(result, dict):
221            payload = result.get("payload")
222            if isinstance(payload, str):
223                return json.loads(payload)
224            return payload
225        return result
226
227    # --- Execute webhook ---
228
229    async def execute_webhook(
230        self,
231        webhook_id: str,
232        *,
233        body: Any = None,
234        headers: dict[str, str] | None = None,
235        method: str = "POST",
236    ) -> Any:
237        """Execute a webhook.
238
239        Calls a webhook endpoint defined in a Squid backend service
240        using the ``@webhook()`` decorator.
241
242        Args:
243            webhook_id: The webhook ID as defined in the ``@webhook("id")`` decorator.
244            body: The request body to send. Must be JSON-serializable.
245            headers: Extra HTTP headers to include in the request.
246            method: The HTTP method to use (default ``"POST"``).
247
248        Returns:
249            The webhook's response body, deserialized from JSON.
250
251        Raises:
252            SquidHttpError: If the webhook is not found or returns an error.
253
254        Example::
255
256            result = await squid.execute_webhook("my-hook", body={"key": "value"})
257        """
258        return await self._http.post(
259            f"webhooks/{webhook_id}",
260            body,
261            extra_headers=headers,
262        )
263
264    def get_webhook_url(self, webhook_id: str) -> str:
265        """Get the full URL for a webhook endpoint.
266
267        Useful for providing the webhook URL to external services that
268        need to send HTTP requests to your Squid backend.
269
270        Args:
271            webhook_id: The webhook ID.
272
273        Returns:
274            The full URL string (e.g.,
275            ``"https://myapp.us-east-1.aws.squid.cloud/webhooks/my-hook"``).
276
277        Example::
278
279            url = squid.get_webhook_url("stripe-events")
280            # url == "https://myapp-dev.us-east-1.aws.squid.cloud/webhooks/stripe-events"
281        """
282        return build_url(self._region, self._full_app_id, f"webhooks/{webhook_id}")
283
284    # --- AI Query ---
285
286    async def execute_ai_query(
287        self,
288        integration_id: str,
289        prompt: str,
290        *,
291        options: AiQueryOptions | None = None,
292        response_format: dict[str, Any] | None = None,
293    ) -> dict:
294        """Execute an AI-powered database query.
295
296        Uses AI to generate and execute database queries based on a
297        natural language prompt.
298
299        Args:
300            integration_id: The database integration ID to query against.
301            prompt: A natural language description of the data you want.
302            options: Optional :class:`AiQueryOptions` controlling collection
303                selection, query generation (including ``allowClarification``),
304                result analysis (including ``enableCodeInterpreter``), memory,
305                AI validation, per-stage model overrides via ``aiOptions``,
306                and custom instructions.
307            response_format: Optional structured output format, e.g.,
308                ``{"type": "json_schema", "schema": {...}}``.
309
310        Returns:
311            An ``AiQueryResponse`` dict containing:
312                - ``answer`` (str): The AI-generated answer.
313                - ``explanation`` (str): How the answer was derived.
314                - ``executedQueries`` (list): The actual queries that were run.
315                - ``success`` (bool): Whether the query succeeded.
316                - ``usedCodeInterpreter`` (bool): Whether the code
317                  interpreter was used to analyze results.
318                - ``clarificationQuestion`` (str): If the AI needs more info.
319
320        Example::
321
322            result = await squid.execute_ai_query(
323                "my-database",
324                "How many users signed up last week?",
325                options={
326                    "selectCollectionsOptions": {"collectionsToUse": ["users"]},
327                    "analyzeResultsOptions": {"enableCodeInterpreter": True},
328                },
329            )
330            print(result["answer"])
331        """
332        return await execute_ai_query(
333            self._http,
334            integration_id,
335            prompt,
336            options=options,
337            response_format=response_format,
338        )
339
340    # --- Lifecycle ---
341
342    async def close(self) -> None:
343        """Close the HTTP client and release resources.
344
345        Should be called when the Squid client is no longer needed.
346        Alternatively, use the client as an async context manager.
347        """
348        await self._http.close()
349
350    async def __aenter__(self) -> Squid:
351        """Enter async context manager."""
352        return self
353
354    async def __aexit__(self, *args: Any) -> None:
355        """Exit async context manager and close resources."""
356        await self.close()

Squid Cloud Python client.

The main entry point for interacting with the Squid Cloud platform. Provides access to AI agents, knowledge bases, web utilities, matchmaking, document extraction, backend functions, and webhooks.

Usage::

squid = Squid(
    app_id="my-app",
    api_key="sk-...",
    region="us-east-1.aws",
    environment_id="dev",
)

# AI agent
response = await squid.ai().agent("my-agent").ask("Hello!")

# Execute backend function
result = await squid.execute_function("MyService:greet", "World")

# Web search
results = await squid.web().ai_search("latest news")

# Cleanup
await squid.close()

Can also be used as an async context manager::

async with Squid(app_id="my-app", api_key="sk-...", region="us-east-1.aws") as squid:
    result = await squid.ai().agent("my-agent").ask("Hello!")
Squid( app_id: str, region: str, api_key: str | None = None, environment_id: str | None = None, squid_developer_id: str | None = None)
58    def __init__(
59        self,
60        app_id: str,
61        region: str,
62        api_key: str | None = None,
63        environment_id: str | None = None,
64        squid_developer_id: str | None = None,
65    ) -> None:
66        """Initialize the Squid client.
67
68        Args:
69            app_id: The Squid application ID.
70            region: The deployment region (e.g., 'us-east-1.aws', 'local').
71            api_key: API key for authentication. Required for most operations.
72            environment_id: Environment identifier (e.g., 'dev', 'prod').
73                Appended to app_id as '{app_id}-{environment_id}'.
74            squid_developer_id: Developer identifier for local development.
75                Appended to app_id as '{app_id}-{environment_id}-{developer_id}'.
76        """
77        self._app_id = app_id
78        self._region = region
79        self._environment_id = environment_id
80        self._squid_developer_id = squid_developer_id
81
82        full_app_id = app_id
83        if environment_id:
84            full_app_id = f"{app_id}-{environment_id}"
85        if squid_developer_id:
86            full_app_id = f"{full_app_id}-{squid_developer_id}"
87
88        self._full_app_id = full_app_id
89        self._http = HttpTransport(
90            app_id=full_app_id,
91            region=region,
92            api_key=api_key,
93        )

Initialize the Squid client.

Arguments:
  • app_id: The Squid application ID.
  • region: The deployment region (e.g., 'us-east-1.aws', 'local').
  • api_key: API key for authentication. Required for most operations.
  • environment_id: Environment identifier (e.g., 'dev', 'prod'). Appended to app_id as '{app_id}-{environment_id}'.
  • squid_developer_id: Developer identifier for local development. Appended to app_id as '{app_id}-{environment_id}-{developer_id}'.
app_id: str
95    @property
96    def app_id(self) -> str:
97        """The application ID (without environment/developer suffix)."""
98        return self._app_id

The application ID (without environment/developer suffix).

region: str
100    @property
101    def region(self) -> str:
102        """The deployment region."""
103        return self._region

The deployment region.

client_id: str
105    @property
106    def client_id(self) -> str:
107        """The unique client instance ID (UUID generated per Squid instance)."""
108        return self._http.client_id

The unique client instance ID (UUID generated per Squid instance).

def ai(self) -> AiClient:
112    def ai(self) -> AiClient:
113        """Access AI operations.
114
115        Provides access to AI agents, knowledge bases, image generation,
116        and audio transcription/synthesis.
117
118        Returns:
119            An :class:`AiClient` instance.
120
121        Example::
122
123            agent = squid.ai().agent("my-agent")
124            response = await agent.ask("What is the weather?")
125
126            kb = squid.ai().knowledge_base("my-kb")
127            results = await kb.search("query")
128        """
129        return AiClient(self._http)

Access AI operations.

Provides access to AI agents, knowledge bases, image generation, and audio transcription/synthesis.

Returns:

An AiClient instance.

Example::

agent = squid.ai().agent("my-agent")
response = await agent.ask("What is the weather?")

kb = squid.ai().knowledge_base("my-kb")
results = await kb.search("query")
def web(self) -> WebClient:
131    def web(self) -> WebClient:
132        """Access web utilities.
133
134        Provides AI-powered web search, URL content extraction,
135        and short URL management.
136
137        Returns:
138            A :class:`WebClient` instance.
139
140        Example::
141
142            content = await squid.web().get_url_content("https://example.com")
143            results = await squid.web().ai_search("latest AI news")
144        """
145        return WebClient(self._http)

Access web utilities.

Provides AI-powered web search, URL content extraction, and short URL management.

Returns:

A WebClient instance.

Example::

content = await squid.web().get_url_content("https://example.com")
results = await squid.web().ai_search("latest AI news")
def matchmaking(self) -> MatchmakingClient:
147    def matchmaking(self) -> MatchmakingClient:
148        """Access AI-powered matchmaking.
149
150        Provides match maker management, entity CRUD,
151        and AI-powered entity matching.
152
153        Returns:
154            A :class:`MatchmakingClient` instance.
155
156        Example::
157
158            mm = squid.matchmaking()
159            await mm.create_match_maker("jobs", "Job matching", categories=[...])
160            matches = await mm.find_matches("jobs", "entity-1")
161        """
162        return MatchmakingClient(self._http)

Access AI-powered matchmaking.

Provides match maker management, entity CRUD, and AI-powered entity matching.

Returns:

A MatchmakingClient instance.

Example::

mm = squid.matchmaking()
await mm.create_match_maker("jobs", "Job matching", categories=[...])
matches = await mm.find_matches("jobs", "entity-1")
def extraction(self) -> ExtractionClient:
164    def extraction(self) -> ExtractionClient:
165        """Access document extraction and PDF creation.
166
167        Provides PDF generation from HTML or URLs, and structured data
168        extraction from documents (PDF, DOCX, images, etc.).
169
170        Returns:
171            An :class:`ExtractionClient` instance.
172
173        Example::
174
175            pdf = await squid.extraction().create_pdf_from_html("<h1>Hello</h1>")
176            data = await squid.extraction().extract_data_from_document_url(
177                "https://example.com/doc.pdf"
178            )
179        """
180        return ExtractionClient(self._http)

Access document extraction and PDF creation.

Provides PDF generation from HTML or URLs, and structured data extraction from documents (PDF, DOCX, images, etc.).

Returns:

An ExtractionClient instance.

Example::

pdf = await squid.extraction().create_pdf_from_html("<h1>Hello</h1>")
data = await squid.extraction().extract_data_from_document_url(
    "https://example.com/doc.pdf"
)
async def execute_function(self, function_name: str, *params: Any) -> Any:
184    async def execute_function(
185        self,
186        function_name: str,
187        *params: Any,
188    ) -> Any:
189        """Execute a backend function by name.
190
191        Calls a function defined in a Squid backend service using the
192        ``@executable()`` decorator.
193
194        Args:
195            function_name: The service function name in the format
196                ``"ClassName:methodName"`` (e.g., ``"MyService:greet"``).
197            *params: Positional arguments to pass to the function.
198                Must be JSON-serializable.
199
200        Returns:
201            The function's return value, deserialized from JSON.
202
203        Raises:
204            SquidHttpError: If the function is not found or throws an error.
205
206        Example::
207
208            result = await squid.execute_function("MyService:greet", "World")
209            # result == "Hello, World!"
210
211            data = await squid.execute_function("MyService:getData", 42, True)
212        """
213        result = await self._http.post(
214            f"backend-function/execute?{function_name}",
215            {
216                "functionName": function_name,
217                "paramsArrayStr": json.dumps(list(params)),
218            },
219        )
220        if isinstance(result, dict):
221            payload = result.get("payload")
222            if isinstance(payload, str):
223                return json.loads(payload)
224            return payload
225        return result

Execute a backend function by name.

Calls a function defined in a Squid backend service using the @executable() decorator.

Arguments:
  • function_name: The service function name in the format "ClassName:methodName" (e.g., "MyService:greet").
  • *params: Positional arguments to pass to the function. Must be JSON-serializable.
Returns:

The function's return value, deserialized from JSON.

Raises:
  • SquidHttpError: If the function is not found or throws an error.

Example::

result = await squid.execute_function("MyService:greet", "World")
# result == "Hello, World!"

data = await squid.execute_function("MyService:getData", 42, True)
async def execute_webhook( self, webhook_id: str, *, body: Any = None, headers: dict[str, str] | None = None, method: str = 'POST') -> Any:
229    async def execute_webhook(
230        self,
231        webhook_id: str,
232        *,
233        body: Any = None,
234        headers: dict[str, str] | None = None,
235        method: str = "POST",
236    ) -> Any:
237        """Execute a webhook.
238
239        Calls a webhook endpoint defined in a Squid backend service
240        using the ``@webhook()`` decorator.
241
242        Args:
243            webhook_id: The webhook ID as defined in the ``@webhook("id")`` decorator.
244            body: The request body to send. Must be JSON-serializable.
245            headers: Extra HTTP headers to include in the request.
246            method: The HTTP method to use (default ``"POST"``).
247
248        Returns:
249            The webhook's response body, deserialized from JSON.
250
251        Raises:
252            SquidHttpError: If the webhook is not found or returns an error.
253
254        Example::
255
256            result = await squid.execute_webhook("my-hook", body={"key": "value"})
257        """
258        return await self._http.post(
259            f"webhooks/{webhook_id}",
260            body,
261            extra_headers=headers,
262        )

Execute a webhook.

Calls a webhook endpoint defined in a Squid backend service using the @webhook() decorator.

Arguments:
  • webhook_id: The webhook ID as defined in the @webhook("id") decorator.
  • body: The request body to send. Must be JSON-serializable.
  • headers: Extra HTTP headers to include in the request.
  • method: The HTTP method to use (default "POST").
Returns:

The webhook's response body, deserialized from JSON.

Raises:
  • SquidHttpError: If the webhook is not found or returns an error.

Example::

result = await squid.execute_webhook("my-hook", body={"key": "value"})
def get_webhook_url(self, webhook_id: str) -> str:
264    def get_webhook_url(self, webhook_id: str) -> str:
265        """Get the full URL for a webhook endpoint.
266
267        Useful for providing the webhook URL to external services that
268        need to send HTTP requests to your Squid backend.
269
270        Args:
271            webhook_id: The webhook ID.
272
273        Returns:
274            The full URL string (e.g.,
275            ``"https://myapp.us-east-1.aws.squid.cloud/webhooks/my-hook"``).
276
277        Example::
278
279            url = squid.get_webhook_url("stripe-events")
280            # url == "https://myapp-dev.us-east-1.aws.squid.cloud/webhooks/stripe-events"
281        """
282        return build_url(self._region, self._full_app_id, f"webhooks/{webhook_id}")

Get the full URL for a webhook endpoint.

Useful for providing the webhook URL to external services that need to send HTTP requests to your Squid backend.

Arguments:
  • webhook_id: The webhook ID.
Returns:

The full URL string (e.g., "https://myapp.us-east-1.aws.squid.cloud/webhooks/my-hook").

Example::

url = squid.get_webhook_url("stripe-events")
# url == "https://myapp-dev.us-east-1.aws.squid.cloud/webhooks/stripe-events"
async def execute_ai_query( self, integration_id: str, prompt: str, *, options: AiQueryOptions | None = None, response_format: dict[str, Any] | None = None) -> dict:
286    async def execute_ai_query(
287        self,
288        integration_id: str,
289        prompt: str,
290        *,
291        options: AiQueryOptions | None = None,
292        response_format: dict[str, Any] | None = None,
293    ) -> dict:
294        """Execute an AI-powered database query.
295
296        Uses AI to generate and execute database queries based on a
297        natural language prompt.
298
299        Args:
300            integration_id: The database integration ID to query against.
301            prompt: A natural language description of the data you want.
302            options: Optional :class:`AiQueryOptions` controlling collection
303                selection, query generation (including ``allowClarification``),
304                result analysis (including ``enableCodeInterpreter``), memory,
305                AI validation, per-stage model overrides via ``aiOptions``,
306                and custom instructions.
307            response_format: Optional structured output format, e.g.,
308                ``{"type": "json_schema", "schema": {...}}``.
309
310        Returns:
311            An ``AiQueryResponse`` dict containing:
312                - ``answer`` (str): The AI-generated answer.
313                - ``explanation`` (str): How the answer was derived.
314                - ``executedQueries`` (list): The actual queries that were run.
315                - ``success`` (bool): Whether the query succeeded.
316                - ``usedCodeInterpreter`` (bool): Whether the code
317                  interpreter was used to analyze results.
318                - ``clarificationQuestion`` (str): If the AI needs more info.
319
320        Example::
321
322            result = await squid.execute_ai_query(
323                "my-database",
324                "How many users signed up last week?",
325                options={
326                    "selectCollectionsOptions": {"collectionsToUse": ["users"]},
327                    "analyzeResultsOptions": {"enableCodeInterpreter": True},
328                },
329            )
330            print(result["answer"])
331        """
332        return await execute_ai_query(
333            self._http,
334            integration_id,
335            prompt,
336            options=options,
337            response_format=response_format,
338        )

Execute an AI-powered database query.

Uses AI to generate and execute database queries based on a natural language prompt.

Arguments:
  • integration_id: The database integration ID to query against.
  • prompt: A natural language description of the data you want.
  • options: Optional AiQueryOptions controlling collection selection, query generation (including allowClarification), result analysis (including enableCodeInterpreter), memory, AI validation, per-stage model overrides via aiOptions, and custom instructions.
  • response_format: Optional structured output format, e.g., {"type": "json_schema", "schema": {...}}.
Returns:

An AiQueryResponse dict containing: - answer (str): The AI-generated answer. - explanation (str): How the answer was derived. - executedQueries (list): The actual queries that were run. - success (bool): Whether the query succeeded. - usedCodeInterpreter (bool): Whether the code interpreter was used to analyze results. - clarificationQuestion (str): If the AI needs more info.

Example::

result = await squid.execute_ai_query(
    "my-database",
    "How many users signed up last week?",
    options={
        "selectCollectionsOptions": {"collectionsToUse": ["users"]},
        "analyzeResultsOptions": {"enableCodeInterpreter": True},
    },
)
print(result["answer"])
async def close(self) -> None:
342    async def close(self) -> None:
343        """Close the HTTP client and release resources.
344
345        Should be called when the Squid client is no longer needed.
346        Alternatively, use the client as an async context manager.
347        """
348        await self._http.close()

Close the HTTP client and release resources.

Should be called when the Squid client is no longer needed. Alternatively, use the client as an async context manager.

class SquidHttpError(builtins.Exception):
61class SquidHttpError(Exception):
62    """Raised when the Squid API returns an HTTP error response (status >= 400).
63
64    Attributes:
65        status_code: The HTTP status code.
66        url: The URL that was requested.
67        body: The parsed response body, if available.
68    """
69
70    def __init__(self, status_code: int, message: str, url: str, body: Any = None):
71        self.status_code = status_code
72        self.url = url
73        self.body = body
74        super().__init__(f"HTTP {status_code}: {message} (url={url})")

Raised when the Squid API returns an HTTP error response (status >= 400).

Attributes:
  • status_code: The HTTP status code.
  • url: The URL that was requested.
  • body: The parsed response body, if available.
SquidHttpError(status_code: int, message: str, url: str, body: Any = None)
70    def __init__(self, status_code: int, message: str, url: str, body: Any = None):
71        self.status_code = status_code
72        self.url = url
73        self.body = body
74        super().__init__(f"HTTP {status_code}: {message} (url={url})")
status_code
url
body
class StableDiffusionOptions(typing.TypedDict):
794class StableDiffusionOptions(TypedDict, total=False):
795    """Options for Stable Diffusion Core image generation."""
796
797    modelName: Literal["stable-diffusion-core"]
798    aspectRatio: Literal["16:9", "1:1", "21:9", "2:3", "3:2", "4:5", "5:4", "9:16", "9:21"]
799    negativePrompt: str
800    seed: int
801    stylePreset: str
802    outputFormat: str

Options for Stable Diffusion Core image generation.

modelName: Literal['stable-diffusion-core']
aspectRatio: Literal['16:9', '1:1', '21:9', '2:3', '3:2', '4:5', '5:4', '9:16', '9:21']
negativePrompt: str
seed: int
stylePreset: str
outputFormat: str
class TextContextRequest(typing.TypedDict):
598class TextContextRequest(TypedDict, total=False):
599    """Request to upsert a text context."""
600
601    contextId: str
602    type: Literal["text"]
603    title: str
604    text: str
605    metadata: dict[str, Any]
606    options: AiContextTextOptions

Request to upsert a text context.

contextId: str
type: Literal['text']
title: str
text: str
metadata: dict[str, typing.Any]
class UpsertAgentOptions(typing.TypedDict):
407class UpsertAgentOptions(TypedDict, total=False):
408    """Options for creating/updating an agent."""
409
410    description: str
411    """Description of the agent's purpose."""
412    isPublic: bool
413    """Whether the agent is publicly accessible."""
414    auditLog: bool
415    """Enable audit logging."""
416    auditLogFullContext: bool
417    """Record the full agent context (system instructions and retrieved knowledge-base content) in the audit log."""
418    apiKey: str
419    """API key for this agent."""
420    mcpServer: AiAgentMcpServerConfig
421    """Optional configuration for exposing the agent as an MCP server."""
422    options: AiChatOptions
423    """Default chat options."""

Options for creating/updating an agent.

description: str

Description of the agent's purpose.

isPublic: bool

Whether the agent is publicly accessible.

auditLog: bool

Enable audit logging.

auditLogFullContext: bool

Record the full agent context (system instructions and retrieved knowledge-base content) in the audit log.

apiKey: str

API key for this agent.

Optional configuration for exposing the agent as an MCP server.

options: AiChatOptions

Default chat options.

VectorDbType = typing.Literal['postgres', 'mongoAtlas']
class WebAiSearchResponse(typing.TypedDict):
912class WebAiSearchResponse(TypedDict):
913    """Response from AI web search."""
914
915    markdownText: str
916    citedUrls: list[dict[str, str]]
917    """Pages the answer cites. A terse answer cites nothing even when the search ran."""
918
919    consultedUrls: list[str]
920    """Pages the search opened, whether or not the answer cites them."""

Response from AI web search.

markdownText: str
citedUrls: list[dict[str, str]]

Pages the answer cites. A terse answer cites nothing even when the search ran.

consultedUrls: list[str]

Pages the search opened, whether or not the answer cites them.

class WebClient:
16class WebClient:
17    """Web utilities: AI search, URL content, short URLs."""
18
19    def __init__(self, http: HttpTransport) -> None:
20        self._http = http
21
22    async def ai_search(
23        self, query: str, allowed_domains: list[str] | None = None
24    ) -> WebAiSearchResponse:
25        """Perform an AI-powered web search.
26
27        When allowed_domains is provided, the search is restricted to those domains
28        (subdomains included) and every URL in the response -- cited or consulted --
29        belongs to one of them. Squid enforces this over the consulted pages;
30        citations arrive already restricted.
31
32        Returns WebAiSearchResponse with 'markdownText', 'citedUrls' and 'consultedUrls'.
33        """
34        body: dict[str, object] = {"query": query}
35        if allowed_domains is not None:
36            body["allowedDomains"] = allowed_domains
37        return await self._http.post("squid-api/v1/web/aiSearch", body)
38
39    async def get_url_content(self, url: str) -> str:
40        """Fetch and extract content from a URL as markdown."""
41        result = await self._http.post("squid-api/v1/web/getUrlContent", {"url": url})
42        if isinstance(result, dict):
43            return result.get("markdownText", "")
44        return str(result) if result else ""
45
46    async def create_short_url(
47        self,
48        url: str,
49        *,
50        seconds_to_live: int | None = None,
51        file_extension: str | None = None,
52    ) -> WebShortUrlResponse:
53        """Create a short URL.
54
55        Returns WebShortUrlResponse with 'id', 'shortUrl', 'expiry'.
56        """
57        body: dict = {"url": url}
58        if seconds_to_live is not None:
59            body["secondsToLive"] = seconds_to_live
60        if file_extension is not None:
61            body["fileExtension"] = file_extension
62        return await self._http.post("squid-api/v1/web/createShortUrl", body)
63
64    async def create_short_urls(
65        self,
66        urls: list[str],
67        *,
68        seconds_to_live: int | None = None,
69        file_extension: str | None = None,
70    ) -> WebShortUrlBulkResponse:
71        """Create multiple short URLs in bulk.
72
73        Returns WebShortUrlBulkResponse with 'ids', 'shortUrls', 'expiry'.
74        """
75        body: dict = {"urls": urls}
76        if seconds_to_live is not None:
77            body["secondsToLive"] = seconds_to_live
78        if file_extension is not None:
79            body["fileExtension"] = file_extension
80        return await self._http.post("squid-api/v1/web/createShortUrls", body)
81
82    async def delete_short_url(self, url_id: str) -> None:
83        """Delete a short URL."""
84        await self._http.post("squid-api/v1/web/deleteShortUrl", {"id": url_id})
85
86    async def delete_short_urls(self, url_ids: list[str]) -> None:
87        """Delete multiple short URLs."""
88        await self._http.post("squid-api/v1/web/deleteShortUrls", {"ids": url_ids})

Web utilities: AI search, URL content, short URLs.

WebClient(http: squidcloud.http.HttpTransport)
19    def __init__(self, http: HttpTransport) -> None:
20        self._http = http
async def get_url_content(self, url: str) -> str:
39    async def get_url_content(self, url: str) -> str:
40        """Fetch and extract content from a URL as markdown."""
41        result = await self._http.post("squid-api/v1/web/getUrlContent", {"url": url})
42        if isinstance(result, dict):
43            return result.get("markdownText", "")
44        return str(result) if result else ""

Fetch and extract content from a URL as markdown.

async def create_short_url( self, url: str, *, seconds_to_live: int | None = None, file_extension: str | None = None) -> WebShortUrlResponse:
46    async def create_short_url(
47        self,
48        url: str,
49        *,
50        seconds_to_live: int | None = None,
51        file_extension: str | None = None,
52    ) -> WebShortUrlResponse:
53        """Create a short URL.
54
55        Returns WebShortUrlResponse with 'id', 'shortUrl', 'expiry'.
56        """
57        body: dict = {"url": url}
58        if seconds_to_live is not None:
59            body["secondsToLive"] = seconds_to_live
60        if file_extension is not None:
61            body["fileExtension"] = file_extension
62        return await self._http.post("squid-api/v1/web/createShortUrl", body)

Create a short URL.

Returns WebShortUrlResponse with 'id', 'shortUrl', 'expiry'.

async def create_short_urls( self, urls: list[str], *, seconds_to_live: int | None = None, file_extension: str | None = None) -> WebShortUrlBulkResponse:
64    async def create_short_urls(
65        self,
66        urls: list[str],
67        *,
68        seconds_to_live: int | None = None,
69        file_extension: str | None = None,
70    ) -> WebShortUrlBulkResponse:
71        """Create multiple short URLs in bulk.
72
73        Returns WebShortUrlBulkResponse with 'ids', 'shortUrls', 'expiry'.
74        """
75        body: dict = {"urls": urls}
76        if seconds_to_live is not None:
77            body["secondsToLive"] = seconds_to_live
78        if file_extension is not None:
79            body["fileExtension"] = file_extension
80        return await self._http.post("squid-api/v1/web/createShortUrls", body)

Create multiple short URLs in bulk.

Returns WebShortUrlBulkResponse with 'ids', 'shortUrls', 'expiry'.

async def delete_short_url(self, url_id: str) -> None:
82    async def delete_short_url(self, url_id: str) -> None:
83        """Delete a short URL."""
84        await self._http.post("squid-api/v1/web/deleteShortUrl", {"id": url_id})

Delete a short URL.

async def delete_short_urls(self, url_ids: list[str]) -> None:
86    async def delete_short_urls(self, url_ids: list[str]) -> None:
87        """Delete multiple short URLs."""
88        await self._http.post("squid-api/v1/web/deleteShortUrls", {"ids": url_ids})

Delete multiple short URLs.

class WebShortUrlBulkResponse(typing.TypedDict):
904class WebShortUrlBulkResponse(TypedDict):
905    """Response from creating bulk short URLs."""
906
907    ids: list[str]
908    shortUrls: list[str]
909    expiry: str

Response from creating bulk short URLs.

ids: list[str]
shortUrls: list[str]
expiry: str
class WebShortUrlResponse(typing.TypedDict):
896class WebShortUrlResponse(TypedDict):
897    """Response from creating a short URL."""
898
899    id: str
900    shortUrl: str
901    expiry: str

Response from creating a short URL.

id: str
shortUrl: str
expiry: str