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

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)
184    def __init__(self, http: HttpTransport, agent_id: str) -> None:
185        self._http = http
186        self._agent_id = agent_id
async def ask( self, prompt: str, options: AiChatOptions | AiAskOptions | None = None) -> str:
190    async def ask(
191        self,
192        prompt: str,
193        options: AiChatOptions | AiAskOptions | None = None,
194    ) -> str:
195        """Ask the agent a question and get a text response.
196
197        Args:
198            prompt: The user's question or instruction.
199            options: Chat options controlling model, temperature, memory,
200                functions, guardrails, and more. See :class:`AiChatOptions`. With
201                :class:`AiAskOptions`, ``model`` may be an :class:`AiModelRoutingOptions`
202                to let a routing model pick the answering model;
203                :meth:`ask_with_annotations` also reports which one it chose.
204
205        Returns:
206            The agent's response as a string.
207
208        Raises:
209            SquidHttpError: If the agent is not found or the request fails.
210
211        Example::
212
213            response = await agent.ask(
214                "Summarize this document",
215                options={
216                    "model": "gemini-3.8-flash",
217                    "temperature": 0.3,
218                    "memoryOptions": {"memoryId": "session-1", "memoryMode": "read-write"},
219                },
220            )
221        """
222        result = await self._http.post(
223            "squid-api/v1/ai/agent/ask",
224            {"agentId": self._agent_id, "prompt": prompt, "options": options or {}},
225        )
226        return result.get("responseString", "") if result else ""

Ask the agent a question and get a text response.

Arguments:
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 | AiAskOptions | None = None) -> dict:
228    async def ask_with_annotations(
229        self,
230        prompt: str,
231        options: AiChatOptions | AiAskOptions | None = None,
232    ) -> dict:
233        """Ask the agent and get a response with annotations.
234
235        Annotations include file references, citations, and other metadata
236        the agent may attach to its response.
237
238        Args:
239            prompt: The user's question or instruction.
240            options: Chat options. See :class:`AiChatOptions` and :class:`AiAskOptions`.
241
242        Returns:
243            A dict with:
244                - ``responseString`` (str): The text response.
245                - ``annotations`` (dict): Annotation metadata keyed by ID.
246                - ``routing`` (:class:`AiModelRoutingResult`): Present when ``model``
247                  was an :class:`AiModelRoutingOptions`: the chosen model and the
248                  routing call's usage.
249
250        Example::
251
252            result = await agent.ask_with_annotations("Find relevant docs")
253            print(result["responseString"])
254            for ann_id, ann in result.get("annotations", {}).items():
255                print(f"  Annotation: {ann}")
256        """
257        return await self._http.post(
258            "squid-api/v1/ai/agent/askWithAnnotations",
259            {"agentId": self._agent_id, "prompt": prompt, "options": options or {}},
260        )

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:
Returns:

A dict with: - responseString (str): The text response. - annotations (dict): Annotation metadata keyed by ID. - routing (AiModelRoutingResult): Present when model was an AiModelRoutingOptions: the chosen model and the routing call's usage.

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:
264    async def get(self) -> dict | None:
265        """Get the agent's configuration.
266
267        Returns:
268            An ``AiAgent`` dict with keys: ``id``, ``createdAt``, ``updatedAt``,
269            ``description``, ``isPublic``, ``auditLog``, ``auditLogFullContext``,
270            ``options``, ``apiKey``.
271            Returns ``None`` if the agent does not exist.
272        """
273        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:
275    async def upsert(
276        self,
277        *,
278        description: str | None = None,
279        is_public: bool | None = None,
280        audit_log: bool | None = None,
281        audit_log_full_context: bool | None = None,
282        api_key: str | None = None,
283        mcp_server: AiAgentMcpServerConfig | None = None,
284        options: AiChatOptions | None = None,
285    ) -> None:
286        """Create or update the agent.
287
288        If the agent does not exist, it is created. If it exists, the provided
289        fields are updated (fields set to ``None`` are left unchanged).
290
291        Args:
292            description: A description of the agent's purpose or capabilities.
293            is_public: Whether the agent is publicly accessible (default ``False``).
294            audit_log: Enable audit logging for the agent's activities.
295            audit_log_full_context: Record the full agent context (system
296                instructions and retrieved knowledge-base content) in the audit
297                log; requires ``audit_log``.
298            api_key: Optional API key used specifically for this agent.
299            mcp_server: Configuration for exposing the agent as an MCP server
300                at ``/mcp/<agentId>``. See :class:`AiAgentMcpServerConfig`.
301            options: Default chat options applied to every request unless
302                overridden per-call. See :class:`AiChatOptions`.
303
304        Example::
305
306            await agent.upsert(
307                description="Customer support agent",
308                options={"model": "gemini-3.8-flash", "temperature": 0.5},
309            )
310        """
311        body: dict[str, Any] = {"id": self._agent_id}
312        if description is not None:
313            body["description"] = description
314        if is_public is not None:
315            body["isPublic"] = is_public
316        if audit_log is not None:
317            body["auditLog"] = audit_log
318        if audit_log_full_context is not None:
319            body["auditLogFullContext"] = audit_log_full_context
320        if api_key is not None:
321            body["apiKey"] = api_key
322        if mcp_server is not None:
323            body["mcpServer"] = mcp_server
324        if options is not None:
325            body["options"] = options
326        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:
328    async def delete(self) -> None:
329        """Delete the agent permanently."""
330        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:
332    async def update_instructions(self, instructions: str) -> None:
333        """Update the agent's system instructions.
334
335        Args:
336            instructions: The new system prompt / instructions text.
337        """
338        await self._http.post(
339            "squid-api/v1/ai/agent/updateInstructions",
340            {"agentId": self._agent_id, "instructions": instructions},
341        )

Update the agent's system instructions.

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

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:
355    async def update_connected_agents(
356        self, connected_agents: list[AiConnectedAgentMetadata]
357    ) -> None:
358        """Update the list of connected agents.
359
360        Connected agents can be called by this agent during conversations.
361
362        Args:
363            connected_agents: List of ``{'agentId': str, 'description': str}`` dicts.
364        """
365        await self._http.post(
366            "squid-api/v1/ai/agent/updateConnectedAgents",
367            {"agentId": self._agent_id, "connectedAgents": connected_agents},
368        )

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:
370    async def update_guardrails(self, guardrails: GuardrailsOptions) -> None:
371        """Update the agent's guardrail settings.
372
373        Args:
374            guardrails: A :class:`GuardrailsOptions` dict with optional keys:
375                ``custom``, ``disablePii``, ``professionalTone``,
376                ``offTopicAnswers``, ``disableProfanity``.
377
378        Example::
379
380            await agent.update_guardrails(
381                {
382                    "professionalTone": True,
383                    "disableProfanity": True,
384                }
385            )
386        """
387        await self._http.post(
388            "squid-api/v1/ai/agent/updateGuardrails",
389            {"agentId": self._agent_id, "guardrails": guardrails},
390        )

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:
392    async def update_custom_guardrails(self, custom_guardrail: str) -> None:
393        """Update the custom guardrail instruction text.
394
395        Args:
396            custom_guardrail: Free-form guardrail instruction string.
397        """
398        await self._http.post(
399            "squid-api/v1/ai/agent/updateCustomGuardrails",
400            {"agentId": self._agent_id, "customGuardrail": custom_guardrail},
401        )

Update the custom guardrail instruction text.

Arguments:
  • custom_guardrail: Free-form guardrail instruction string.
async def delete_custom_guardrails(self) -> None:
403    async def delete_custom_guardrails(self) -> None:
404        """Delete the custom guardrail, reverting to defaults."""
405        await self._http.post(
406            "squid-api/v1/ai/agent/deleteCustomGuardrails",
407            {"agentId": self._agent_id},
408        )

Delete the custom guardrail, reverting to defaults.

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

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]:
441    async def list_revisions(self) -> list[dict]:
442        """List all revisions of this agent.
443
444        Returns:
445            A list of ``AiAgentRevision`` dicts, each containing:
446            ``agentId``, ``revisionNumber``, ``action``, ``createdAt``,
447            ``agentSnapshot``.
448        """
449        result = await self._http.get(f"squid-api/v1/ai/agent/revisions/{self._agent_id}")
450        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:
452    async def restore_revision(self, revision_number: int) -> None:
453        """Restore the agent to a previous revision.
454
455        Args:
456            revision_number: The revision number to restore.
457        """
458        await self._http.post(
459            "squid-api/v1/ai/agent/restoreRevision",
460            {"agentId": self._agent_id, "revisionNumber": revision_number},
461        )

Restore the agent to a previous revision.

Arguments:
  • revision_number: The revision number to restore.
async def delete_revision(self, revision_number: int) -> None:
463    async def delete_revision(self, revision_number: int) -> None:
464        """Delete a specific revision.
465
466        Args:
467            revision_number: The revision number to delete.
468        """
469        await self._http.post(
470            "squid-api/v1/ai/agent/deleteRevision",
471            {"agentId": self._agent_id, "revisionNumber": revision_number},
472        )

Delete a specific revision.

Arguments:
  • revision_number: The revision number to delete.
class AiAgent(typing.TypedDict):
474class AiAgent(TypedDict, total=False):
475    """A definition of an AI agent with its properties and default chat options.
476
477    Returned by :meth:`AiClient.list_agents`.
478    """
479
480    id: str
481    """The unique identifier of the AI agent. Required."""
482    createdAt: str
483    """ISO 8601 timestamp of when the agent was created. Required."""
484    updatedAt: str
485    """ISO 8601 timestamp of when the agent was last updated. Required."""
486    description: str
487    """An optional description of the agent's purpose or capabilities."""
488    isPublic: bool
489    """Whether the agent is publicly accessible; defaults to False."""
490    auditLog: bool
491    """Whether audit logging is enabled for the agent's activities; defaults to False."""
492    auditLogFullContext: bool
493    """Whether the full agent context (system instructions and retrieved knowledge-base content) is recorded in the audit log; requires auditLog; defaults to False."""
494    options: AiChatOptions
495    """The agent's default chat options, overridable by the user during use. Required."""
496    apiKey: str
497    """Optional API key used specifically for operations on this agent."""
498    mcpServer: AiAgentMcpServerConfig
499    """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):
440class AiAgentMcpServerConfig(TypedDict, total=False):
441    """Configuration for exposing an AI agent as an MCP server at ``/mcp/<agentId>``."""
442
443    enabled: bool
444    """Whether the agent is exposed as an MCP server. Required."""
445    description: str
446    """Description of the MCP server, served as the ``instructions`` field of the MCP initialize result."""
447    toolDescription: str
448    """Description of the MCP ``ask`` tool, exposed in the tools manifest."""
449    oauthIntegrationId: str
450    """ID of an auth integration used to OAuth-protect the MCP server."""
451    requireApiKey: bool
452    """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
AiAskModelSelection = str | IntegrationModelSpec | AiModelRoutingOptions
class AiAskOptions(squidcloud.types._AiChatOptionsBase):
334class AiAskOptions(_AiChatOptionsBase, total=False):
335    """Options of :meth:`AgentClient.ask` and :meth:`AgentClient.ask_with_annotations`.
336
337    The same fields as :class:`AiChatOptions`, except that ``model`` may also be an
338    :class:`AiModelRoutingOptions` routing choice.
339    """
340
341    model: AiAskModelSelection
342    """LLM model to use: a name, an IntegrationModelSpec, or an AiModelRoutingOptions."""

Options of AgentClient.ask() and AgentClient.ask_with_annotations().

The same fields as AiChatOptions, except that model may also be an AiModelRoutingOptions routing choice.

LLM model to use: a name, an IntegrationModelSpec, or an AiModelRoutingOptions.

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(squidcloud.types._AiChatOptionsBase):
323class AiChatOptions(_AiChatOptionsBase, total=False):
324    """Full chat options for AI agent chat operations and agent profiles.
325
326    Mirrors BaseAiChatOptions from the TypeScript SDK.
327    All fields are optional.
328    """
329
330    model: AiChatModelSelection
331    """LLM model to use. String name or IntegrationModelSpec."""

Full chat options for AI agent chat operations and agent profiles.

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

model: str | IntegrationModelSpec

LLM model to use. String name or IntegrationModelSpec.

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

Get a client for image generation and processing.

Returns:

An ImageClient instance.

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

Get a client for audio transcription and speech synthesis.

Returns:

An AudioClient instance.

async def list_agents(self) -> list[AiAgent]:
 93    async def list_agents(self) -> list[AiAgent]:
 94        """List all AI agents defined for the application.
 95
 96        Returns:
 97            A list of ``AiAgent`` dicts. Empty list if no agents are defined.
 98        """
 99        result = await self._http.get("squid-api/v1/ai/agent/listAgents")
100        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]:
102    async def list_knowledge_bases(self) -> list[AiKnowledgeBase]:
103        """List all AI knowledge bases defined for the application.
104
105        Returns:
106            A list of ``AiKnowledgeBase`` dicts. Empty list if no knowledge
107            bases are defined.
108        """
109        result = await self._http.get("squid-api/v1/ai/knowledge-base/listKnowledgeBases")
110        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]:
112    async def list_chat_models(self, include_deprecated: bool = False) -> list[ModelIdSpec]:
113        """List all AI chat models available to the application.
114
115        Includes both Squid-provided vendor models and any custom integration
116        models configured for the app.
117
118        Args:
119            include_deprecated: When True, deprecated vendor models are
120                included and marked with ``replacedBy``. Defaults to False.
121
122        Returns:
123            A list of ``ModelIdSpec`` dicts, each with ``modelId``, an optional
124            ``integrationId``, and a human-readable ``displayName``. Vendor
125            models also carry a ``description``, and deprecated vendor models a
126            ``replacedBy`` with the active model their calls are routed to.
127        """
128        params = {"includeDeprecated": "true"} if include_deprecated else None
129        result = await self._http.get("squid-api/v1/ai/settings/listChatModels", params=params)
130        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]:
132    async def list_embedding_models(
133        self, include_deprecated: bool = False
134    ) -> list[EmbeddingModelIdSpec]:
135        """List the embedding models a new knowledge base can be created on.
136
137        Includes Squid-provided vendor models and the app's custom integration
138        models. Exactly one entry carries ``isDefault``.
139
140        Args:
141            include_deprecated: When True, deprecated vendor models are
142                included and marked with ``replacedBy``. Defaults to False.
143
144        Returns:
145            A list of ``EmbeddingModelIdSpec`` dicts, each with ``modelId``,
146            ``displayName`` and ``source``, plus ``integrationId`` and
147            ``dimensions`` for integration models.
148        """
149        params = {"includeDeprecated": "true"} if include_deprecated else None
150        result = await self._http.get("squid-api/v1/ai/settings/listEmbeddingModels", params=params)
151        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]:
153    async def list_functions(self) -> list[AiFunctionMetadata]:
154        """List all AI functions registered for the application's deployed bundle.
155
156        Returns:
157            A list of ``AiFunctionMetadata`` dicts. Empty list if no functions
158            are registered.
159        """
160        result = await self._http.get("squid-api/v1/ai/function/listFunctions")
161        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):
683class AiContextFileOptions(TypedDict, total=False):
684    """Options for file context processing."""
685
686    chunkOverlap: int
687    ragType: str

Options for file context processing.

chunkOverlap: int
ragType: str
class AiContextTextOptions(typing.TypedDict):
647class AiContextTextOptions(TypedDict, total=False):
648    """Options for text context processing."""
649
650    chunkOverlap: int
651    """Amount of chunk overlap in characters."""
652    ragType: str
653    """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):
812class AiFunctionAttributes(TypedDict, total=False):
813    """Additional optional readonly metadata for an AI function."""
814
815    integrationType: list[str]
816    """Types of integration this function is used for. Functions with a defined
817    '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):
820class AiFunctionMetadata(TypedDict, total=False):
821    """Metadata describing an AI function available in the application.
822
823    Returned by :meth:`AiClient.list_functions`.
824    """
825
826    serviceFunction: str
827    """The fully qualified name of the function ('ServiceName:functionName'). Required."""
828    description: str
829    """Description of what the function does."""
830    promptId: str
831    """Opaque ID of a registered prompt that supplies this function's description;
832    resolved server-side."""
833    params: list[AiFunctionParam]
834    """Parameters that the function accepts. Required."""
835    attributes: AiFunctionAttributes
836    """Additional attributes for the function."""
837    categories: list[str]
838    """Categories this function belongs to."""
839    internal: bool
840    """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):
795class AiFunctionParam(TypedDict, total=False):
796    """Defines the structure of a parameter for an AI function."""
797
798    name: str
799    """Name of the parameter. Required."""
800    description: str
801    """Description of the parameter's purpose. Required."""
802    type: AiFunctionParamType
803    """Data type of the parameter. Required."""
804    required: bool
805    """Indicates if the parameter is mandatory. Required."""
806    enum: list[str]
807    """List of possible values for the parameter, if applicable."""
808    schema: dict[str, Any]
809    """JSON Schema of an ``object`` parameter's value, which Squid validates every call against."""

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', 'object']

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.

schema: dict[str, typing.Any]

JSON Schema of an object parameter's value, which Squid validates every call against.

class AiKnowledgeBase(typing.TypedDict):
618class AiKnowledgeBase(TypedDict, total=False):
619    """An AI knowledge base that can be attached to an AI agent.
620
621    Returned by :meth:`AiClient.list_knowledge_bases`.
622    """
623
624    id: str
625    """The unique identifier of the knowledge base. Required."""
626    appId: str
627    """The app ID that the knowledge base belongs to. Required."""
628    description: str
629    """The user's description of the knowledge base. Required."""
630    metadataFields: list[AiKnowledgeBaseMetadataField]
631    """Predefined metadata fields that can be used for filtering. Required."""
632    embeddingModel: AiEmbeddingsModelSelection
633    """The embedding model used by this knowledge base. Required."""
634    chatModel: AiChatModelSelection
635    """The model used when asking questions of this knowledge base. Required."""
636    vectorDbType: VectorDbType
637    """The vector store backend the knowledge base reads/writes from. Set at creation
638    and immutable thereafter. Absent on older records."""
639    graphRag: AiKnowledgeBaseGraphConfig
640    """Opt-in GraphRAG configuration. Only honored for ``vectorDbType: 'mongoAtlas'``
641    knowledge bases; enables per-chunk entity/relationship extraction at ingest and
642    ``searchMode: 'graph'`` at query time."""
643    updatedAt: str
644    """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):
557class AiKnowledgeBaseGraphConceptsConfig(TypedDict, total=False):
558    """Configuration of the graph's concept layer (facet taxonomies)."""
559
560    facets: Union[list[str], Literal["auto"]]
561    """Which metadata fields become value facets. ``'auto'`` (the default) profiles the KB's
562    context metadata and selects the categorical fields; an explicit list overrides
563    auto-selection; an empty list disables value facets."""
564    pathFacets: list[AiKnowledgeBaseGraphPathFacetConfig]
565    """Explicit path facets, e.g. ``[{'field': 'folderPath', 'type': 'path'}]``. A path facet
566    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):
581class AiKnowledgeBaseGraphConfig(_AiKnowledgeBaseGraphConfigRequired, total=False):
582    """Per-knowledge-base GraphRAG configuration.
583
584    Opt-in and mutable (unlike ``embeddingModel``/``vectorDbType``); only honored for
585    ``vectorDbType: 'mongoAtlas'`` knowledge bases. Enabling it on a knowledge base that
586    already has content backfills the graph automatically once the KB goes quiet.
587
588    On upsert the supplied value replaces the stored config rather than merging into it,
589    so include every key that should remain set.
590    """
591
592    extractionModel: AiChatModelSelection
593    """Chat model used for per-chunk entity/relationship extraction. Defaults to the server's
594    graph model."""
595    entityTypes: list[str]
596    """Optional domain taxonomy hint (entity types) injected into the extraction prompt."""
597    autoBuildDebounceMs: int
598    """Quiet window (milliseconds) after the last graph activity before the sweep auto-builds
599    the structure. Must be a positive integer. Defaults to the server's window (5 minutes)."""
600    autoBuildPaused: bool
601    """When true, the sweep starts no automatic build, entity resolution included: activity keeps
602    arming the build, and the first quiet window after the flag is cleared runs it. ``rebuild_graph``
603    stays available. To hold only the structural build, use ``structuralBuildSchedule``. Defaults to
604    false."""
605    structuralBuildSchedule: str
606    """When the sweep may start an automatic structural build: ``'debounced'`` (one quiet window
607    after activity), ``'off'`` (manual ``rebuild_graph`` only) or a 5-field cron expression.
608    Entity resolution keeps running either way. Omitted means the server's default."""
609    structuralBuildTimeZone: str
610    """IANA time zone of a cron ``structuralBuildSchedule``. Omitted means the server's default (UTC)."""
611    structuralBuildCatchUpMs: int
612    """How long after a cron slot its structural build may still start (milliseconds); past it, the
613    build waits for the next slot. Omitted means the server's default (3 hours)."""
614    concepts: AiKnowledgeBaseGraphConceptsConfig
615    """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 starts no automatic build, entity resolution included: activity keeps arming the build, and the first quiet window after the flag is cleared runs it. rebuild_graph stays available. To hold only the structural build, use structuralBuildSchedule. Defaults to false.

structuralBuildSchedule: str

When the sweep may start an automatic structural build: 'debounced' (one quiet window after activity), 'off' (manual rebuild_graph only) or a 5-field cron expression. Entity resolution keeps running either way. Omitted means the server's default.

structuralBuildTimeZone: str

IANA time zone of a cron structuralBuildSchedule. Omitted means the server's default (UTC).

structuralBuildCatchUpMs: int

How long after a cron slot its structural build may still start (milliseconds); past it, the build waits for the next slot. Omitted means the server's default (3 hours).

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

class AiKnowledgeBaseGraphFilter(typing.TypedDict):
732class AiKnowledgeBaseGraphFilter(TypedDict):
733    """Graph scope for a knowledge-base search: restricts results to the documents under one
734    concept of the KB's graph. Composes with every ``searchMode``. Requires ``graphRag.enabled``."""
735
736    underConcept: str
737    """The concept to scope to: a facet nodeId (``facet_…`` — exact) or a concept name,
738    resolved server-side (exact, then alias, then similarity). An unresolvable ref fails with
739    ``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):
548class AiKnowledgeBaseGraphPathFacetConfig(
549    _AiKnowledgeBaseGraphPathFacetConfigRequired, total=False
550):
551    """A path facet: a deterministic tree built by splitting a hierarchical path field."""
552
553    separator: str
554    """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):
718class AiKnowledgeBaseGraphSearchOptions(TypedDict, total=False):
719    """Tuning for ``searchMode: 'graph'`` (GraphRAG) retrieval. Ignored for other search modes."""
720
721    seedLimit: int
722    """Entities seeded via vector search before graph expansion. Default 8, max 25."""
723    maxHops: int
724    """Number of hops to expand from each seed (traversal depth + 1). Default 2, max 3."""
725    includeGraphContext: bool
726    """When True, the server attaches the traversed subgraph to the search response as
727    ``graphContext``. Default False. Read it via
728    :meth:`~squidcloud.ai.KnowledgeBaseClient.search_with_graph_context`;
729    :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):
505class AiKnowledgeBaseMetadataField(TypedDict, total=False):
506    """Metadata field definition for a knowledge base."""
507
508    name: str
509    dataType: str
510    required: bool
511    description: str

Metadata field definition for a knowledge base.

name: str
dataType: str
required: bool
description: str
class AiModelRoutingOptions(typing.TypedDict):
221class AiModelRoutingOptions(TypedDict):
222    """Lets a routing model pick which of several vendor models answers an ``ask``.
223
224    The routing model reads the question, the agent's instructions and a card per candidate
225    and names one of them. Only single questions are routed.
226    """
227
228    models: list[AiChatModelName]
229    """At least two distinct vendor model names to choose from."""
230    routingModel: AiChatModelName
231    """The vendor model that makes the choice."""

Lets a routing model pick which of several vendor models answers an ask.

The routing model reads the question, the agent's instructions and a card per candidate and names one of them. Only single questions are routed.

models: list[str]

At least two distinct vendor model names to choose from.

routingModel: str

The vendor model that makes the choice.

class AiModelRoutingResult(typing.TypedDict):
238class AiModelRoutingResult(TypedDict, total=False):
239    """Which model the routing model chose for an ``ask`` and what the routing call cost."""
240
241    model: AiChatModelName
242    """The chosen candidate. A ``backupModel`` may still have served the answer."""
243    routingModel: AiChatModelName
244    """The model that made the choice."""
245    inputTokens: int
246    """Input tokens of the routing call."""
247    outputTokens: int
248    """Output tokens of the routing call."""
249    costUsd: float
250    """Cost of the routing call in USD, when known."""

Which model the routing model chose for an ask and what the routing call cost.

model: str

The chosen candidate. A backupModel may still have served the answer.

routingModel: str

The model that made the choice.

inputTokens: int

Input tokens of the routing call.

outputTokens: int

Output tokens of the routing call.

costUsd: float

Cost of the routing call in USD, when known.

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-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-6-luna'.

allowList: list[str]

Literal values that never count as PII.

class AiQueryAnalyzeResultsOptions(typing.TypedDict):
389class AiQueryAnalyzeResultsOptions(TypedDict, total=False):
390    """Options for the result-analysis stage of AI query."""
391
392    disabled: bool
393    """When true, skip analysis and return raw results only."""
394    enableCodeInterpreter: bool
395    """Enable code interpreter mode (default false)."""
396    aiOptions: AiChatOptions
397    """Customize AI agent behavior used by the stage."""
398    agentId: AiAgentId
399    """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):
375class AiQueryGenerateQueryOptions(TypedDict, total=False):
376    """Options for the query-generation stage of AI query."""
377
378    aiOptions: AiChatOptions
379    """Customize AI agent behavior used by the stage."""
380    maxErrorCorrections: int
381    """Number of retries due to errors in a generated AI query (default 2)."""
382    agentId: AiAgentId
383    """If set, use this agent to generate the query."""
384    allowClarification: bool
385    """When true, allow the AI to ask a clarifying question instead of
386    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):
411class AiQueryOptions(TypedDict, total=False):
412    """Options for configuring AI query execution.
413
414    Mirrors ``AiQueryOptions`` from the TypeScript SDK. All fields optional.
415    """
416
417    instructions: str
418    """Custom instructions applied to all stages unless overridden per-stage."""
419    enableRawResults: bool
420    """Enable raw results output."""
421    selectCollectionsOptions: AiQuerySelectCollectionsOptions
422    """Collection-selection stage options."""
423    generateQueryOptions: AiQueryGenerateQueryOptions
424    """Query-generation stage options."""
425    analyzeResultsOptions: AiQueryAnalyzeResultsOptions
426    """Result-analysis stage options."""
427    sessionContext: AiSessionContext
428    """Session information (``agentId`` may be omitted)."""
429    memoryOptions: AiAgentMemoryOptions
430    """Memory/session configuration."""
431    generateQueriesOnly: bool
432    """If true, return generated queries without executing or analyzing them."""
433    validateWithAiOptions: AiQueryValidateWithAiOptions
434    """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):
364class AiQuerySelectCollectionsOptions(TypedDict, total=False):
365    """Options for the collection-selection stage of AI query."""
366
367    collectionsToUse: list[str]
368    """Restrict query to these collections. Defaults to all collections."""
369    runMode: AiQueryCollectionsSelectionRunMode
370    """Stage behavior: 'default', 'force', or 'disable'."""
371    aiOptions: AiChatOptions
372    """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):
402class AiQueryValidateWithAiOptions(TypedDict, total=False):
403    """Options for AI-based validation of generated queries."""
404
405    enabled: bool
406    """Whether AI validation is enabled."""
407    aiOptions: AiChatOptions
408    """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):
351class AiSessionContext(TypedDict, total=False):
352    """Session context for AI query execution.
353
354    Mirrors the TypeScript ``AiSessionContext`` type. All fields optional
355    here because the query endpoint accepts a partial context (``agentId``
356    may be omitted).
357    """
358
359    clientId: str
360    agentId: str
361    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:
1065class AudioClient:
1066    """Audio transcription and speech synthesis.
1067
1068    Obtained via :meth:`AiClient.audio`.
1069
1070    Example::
1071
1072        text = await squid.ai().audio().transcribe(audio_bytes)
1073        speech = await squid.ai().audio().create_speech("Hello!", options={"voice": "nova"})
1074    """
1075
1076    def __init__(self, http: HttpTransport) -> None:
1077        self._http = http
1078
1079    async def transcribe(
1080        self,
1081        audio_data: bytes,
1082        filename: str = "audio.wav",
1083        content_type: str = "audio/wav",
1084        *,
1085        options: dict[str, Any] | None = None,
1086    ) -> str:
1087        """Transcribe audio to text.
1088
1089        Args:
1090            audio_data: The audio file content as bytes.
1091            filename: The filename (used in the multipart upload).
1092            content_type: The MIME type of the audio file.
1093            options: Provider-specific transcription options.
1094
1095        Returns:
1096            The transcribed text.
1097        """
1098        form_data: dict[str, str] = {
1099            "optionsJson": json.dumps(options or {}),
1100        }
1101        result = await self._http.post_form(
1102            "squid-api/v1/ai/audio/transcribe",
1103            data=form_data,
1104            files=[("file", (filename, audio_data, content_type))],
1105        )
1106        return result if isinstance(result, str) else str(result)
1107
1108    async def create_speech(
1109        self,
1110        text: str,
1111        options: AiAudioCreateSpeechOptions,
1112    ) -> bytes:
1113        """Generate speech audio from text.
1114
1115        Args:
1116            text: The text to convert to speech.
1117            options: Speech generation options. See :class:`AiAudioCreateSpeechOptions`.
1118                Required keys: ``modelName`` (e.g., ``'tts-1'``), ``voice``
1119                (e.g., ``'nova'``, ``'alloy'``).
1120
1121        Returns:
1122            Raw audio file bytes (e.g., MP3 format by default).
1123
1124        Example::
1125
1126            audio_data = (
1127                await squid.ai()
1128                .audio()
1129                .create_speech(
1130                    "Hello!",
1131                    options={
1132                        "modelName": "tts-1",
1133                        "voice": "nova",
1134                    },
1135                )
1136            )
1137            with open("speech.mp3", "wb") as f:
1138                f.write(audio_data)
1139        """
1140        return await self._http.post(
1141            "squid-api/v1/ai/audio/createSpeech",
1142            {"input": text, "options": options},
1143        )

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)
1076    def __init__(self, http: HttpTransport) -> None:
1077        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:
1079    async def transcribe(
1080        self,
1081        audio_data: bytes,
1082        filename: str = "audio.wav",
1083        content_type: str = "audio/wav",
1084        *,
1085        options: dict[str, Any] | None = None,
1086    ) -> str:
1087        """Transcribe audio to text.
1088
1089        Args:
1090            audio_data: The audio file content as bytes.
1091            filename: The filename (used in the multipart upload).
1092            content_type: The MIME type of the audio file.
1093            options: Provider-specific transcription options.
1094
1095        Returns:
1096            The transcribed text.
1097        """
1098        form_data: dict[str, str] = {
1099            "optionsJson": json.dumps(options or {}),
1100        }
1101        result = await self._http.post_form(
1102            "squid-api/v1/ai/audio/transcribe",
1103            data=form_data,
1104            files=[("file", (filename, audio_data, content_type))],
1105        )
1106        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:
1108    async def create_speech(
1109        self,
1110        text: str,
1111        options: AiAudioCreateSpeechOptions,
1112    ) -> bytes:
1113        """Generate speech audio from text.
1114
1115        Args:
1116            text: The text to convert to speech.
1117            options: Speech generation options. See :class:`AiAudioCreateSpeechOptions`.
1118                Required keys: ``modelName`` (e.g., ``'tts-1'``), ``voice``
1119                (e.g., ``'nova'``, ``'alloy'``).
1120
1121        Returns:
1122            Raw audio file bytes (e.g., MP3 format by default).
1123
1124        Example::
1125
1126            audio_data = (
1127                await squid.ai()
1128                .audio()
1129                .create_speech(
1130                    "Hello!",
1131                    options={
1132                        "modelName": "tts-1",
1133                        "voice": "nova",
1134                    },
1135                )
1136            )
1137            with open("speech.mp3", "wb") as f:
1138                f.write(audio_data)
1139        """
1140        return await self._http.post(
1141            "squid-api/v1/ai/audio/createSpeech",
1142            {"input": text, "options": options},
1143        )

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):
949class CreatePdfDimensionsOptions(TypedDict):
950    """PDF output options by custom dimensions."""
951
952    type: Literal["dimensions"]
953    width: int
954    """Width in pixels."""
955    height: int
956    """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):
930class CreatePdfFormatOptions(TypedDict):
931    """PDF output options by format."""
932
933    type: Literal["format"]
934    format: Literal[
935        "letter",
936        "legal",
937        "tabloid",
938        "ledger",
939        "a0",
940        "a1",
941        "a2",
942        "a3",
943        "a4",
944        "a5",
945        "a6",
946    ]

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):
962class ExtractDataFromDocumentOptions(TypedDict, total=False):
963    """Options for document data extraction."""
964
965    extractImages: bool
966    """Whether to extract embedded images. Defaults to True."""
967    imageMinSizePixels: int
968    """Minimum image size to extract."""
969    pageIndexes: list[int]
970    """Specific pages to extract (0-based)."""
971    preferredExtractionMethod: str
972    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):
690class FileContextRequest(TypedDict, total=False):
691    """Request to upsert a file context."""
692
693    contextId: str
694    type: Literal["file"]
695    metadata: dict[str, Any]
696    extractImages: bool
697    """Whether to extract and describe images in the file. Defaults to True.
698
699    This is the opposite of the text context default, which is False: a file's charts and
700    scanned pages carry content its text layer does not, while images referenced from pasted
701    markdown/HTML are usually layout chrome.
702    """
703    imageMinSizePixels: int
704    imageExtractionModel: AiChatModelSelection
705    options: AiContextFileOptions
706    preferredExtractionMethod: str
707    discardOriginalFile: bool
708    scopes: list[str]
709    """Access scopes granting read access to this context. Only meaningful on a knowledge base
710    with ``accessControl.mode: 'enforced'``. Omitted on a re-ingest keeps existing scopes; an
711    empty list makes the context visible to every caller.
712    """

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
imageExtractionModel: str | IntegrationModelSpec
preferredExtractionMethod: str
discardOriginalFile: bool
scopes: list[str]

Access scopes granting read access to this context. Only meaningful on a knowledge base with accessControl.mode: 'enforced'. Omitted on a re-ingest keeps existing scopes; an empty list makes the context visible to every caller.

class FluxOptions(typing.TypedDict):
862class FluxOptions(TypedDict, total=False):
863    """Options for Flux image generation."""
864
865    modelName: Literal["flux-pro-1.1", "flux-kontext-pro"]
866    width: int
867    """Must be multiple of 32, min 256, max 1440."""
868    height: int
869    """Must be multiple of 32, min 256, max 1440."""
870    prompt_upsampling: bool
871    seed: int
872    safety_tolerance: int
873    """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):
846class GptImageOptions(TypedDict, total=False):
847    """Options for OpenAI gpt-image-* family image generation."""
848
849    modelName: Literal[
850        "gpt-image-1",
851        "gpt-image-1-mini",
852        "gpt-image-1.5",
853        "gpt-image-2",
854        "gpt-image-2-2026-04-21",
855        "chatgpt-image-latest",
856    ]
857    quality: Literal["auto", "high", "medium", "low"]
858    size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"]
859    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:
 991class ImageClient:
 992    """Image generation and processing.
 993
 994    Supports DALL-E, Stable Diffusion Core, and Flux models.
 995
 996    Obtained via :meth:`AiClient.image`.
 997
 998    Example::
 999
1000        image_url = (
1001            await squid.ai()
1002            .image()
1003            .generate(
1004                "a cat astronaut on the moon",
1005                options={"modelName": "gpt-image-1", "quality": "high"},
1006            )
1007        )
1008    """
1009
1010    def __init__(self, http: HttpTransport) -> None:
1011        self._http = http
1012
1013    async def generate(
1014        self,
1015        prompt: str,
1016        options: ImageGenerateOptions | None = None,
1017    ) -> str:
1018        """Generate an image from a text prompt.
1019
1020        Args:
1021            prompt: A text description of the image to generate.
1022            options: Provider-specific generation options. Use one of:
1023                - :class:`GptImageOptions`: ``{'modelName': 'gpt-image-1', 'quality': 'high', 'size': '1024x1024'}``
1024                - :class:`FluxOptions`: ``{'modelName': 'flux-pro-1.1', 'width': 1024, 'height': 768}``
1025                - :class:`StableDiffusionOptions`: ``{'modelName': 'stable-diffusion-core', 'aspectRatio': '16:9'}``
1026
1027        Returns:
1028            The URL of the generated image.
1029        """
1030        result = await self._http.post(
1031            "squid-api/v1/ai/image/generate",
1032            {"prompt": prompt, "options": options or {}},
1033        )
1034        return result if isinstance(result, str) else str(result)
1035
1036    async def remove_background(
1037        self,
1038        image_data: bytes,
1039        filename: str = "image.png",
1040        content_type: str = "image/png",
1041    ) -> str:
1042        """Remove the background from an image.
1043
1044        Args:
1045            image_data: The image file content as bytes.
1046            filename: The filename (used in the multipart upload).
1047            content_type: The MIME type of the image.
1048
1049        Returns:
1050            The URL of the processed image with the background removed.
1051        """
1052        result = await self._http.post_form(
1053            "squid-api/v1/ai/image/removeBackground",
1054            data={},
1055            files=[("file", (filename, image_data, content_type))],
1056        )
1057        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)
1010    def __init__(self, http: HttpTransport) -> None:
1011        self._http = http
async def generate( self, prompt: str, options: GptImageOptions | FluxOptions | StableDiffusionOptions | None = None) -> str:
1013    async def generate(
1014        self,
1015        prompt: str,
1016        options: ImageGenerateOptions | None = None,
1017    ) -> str:
1018        """Generate an image from a text prompt.
1019
1020        Args:
1021            prompt: A text description of the image to generate.
1022            options: Provider-specific generation options. Use one of:
1023                - :class:`GptImageOptions`: ``{'modelName': 'gpt-image-1', 'quality': 'high', 'size': '1024x1024'}``
1024                - :class:`FluxOptions`: ``{'modelName': 'flux-pro-1.1', 'width': 1024, 'height': 768}``
1025                - :class:`StableDiffusionOptions`: ``{'modelName': 'stable-diffusion-core', 'aspectRatio': '16:9'}``
1026
1027        Returns:
1028            The URL of the generated image.
1029        """
1030        result = await self._http.post(
1031            "squid-api/v1/ai/image/generate",
1032            {"prompt": prompt, "options": options or {}},
1033        )
1034        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:
1036    async def remove_background(
1037        self,
1038        image_data: bytes,
1039        filename: str = "image.png",
1040        content_type: str = "image/png",
1041    ) -> str:
1042        """Remove the background from an image.
1043
1044        Args:
1045            image_data: The image file content as bytes.
1046            filename: The filename (used in the multipart upload).
1047            content_type: The MIME type of the image.
1048
1049        Returns:
1050            The URL of the processed image with the background removed.
1051        """
1052        result = await self._http.post_form(
1053            "squid-api/v1/ai/image/removeBackground",
1054            data={},
1055            files=[("file", (filename, image_data, content_type))],
1056        )
1057        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):
514class IntegrationEmbeddingModelSpec(TypedDict):
515    """Specifies an embedding model from a specific integration."""
516
517    integrationId: str
518    """The ID of the integration providing the embedding model."""
519    model: str
520    """The model name as recognized by the provider."""
521    dimensions: int
522    """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:
480class KnowledgeBaseClient:
481    """Operations on a single knowledge base.
482
483    Provides methods for managing knowledge base configuration, upserting
484    and searching text/file contexts, and retrieving individual contexts.
485
486    Obtained via :meth:`AiClient.knowledge_base`.
487
488    Example::
489
490        kb = squid.ai().knowledge_base("my-kb")
491        await kb.upsert(description="Product documentation")
492        await kb.upsert_contexts(
493            [
494                {
495                    "contextId": "doc-1",
496                    "type": "text",
497                    "title": "Getting Started",
498                    "text": "Welcome to our product...",
499                }
500            ]
501        )
502        results = await kb.search("How do I get started?")
503    """
504
505    def __init__(self, http: HttpTransport, kb_id: str) -> None:
506        self._http = http
507        self._kb_id = kb_id
508
509    async def get(self) -> dict | None:
510        """Get the knowledge base details.
511
512        Returns:
513            An ``AiKnowledgeBase`` dict with keys: ``id``, ``appId``,
514            ``description``, ``metadataFields``, ``embeddingModel``,
515            ``chatModel``, ``updatedAt``. Returns ``None`` if not found.
516        """
517        return await self._http.get(f"squid-api/v1/ai/knowledge-base/get/{self._kb_id}")
518
519    async def upsert(
520        self,
521        *,
522        description: str | None = None,
523        metadata_fields: list[AiKnowledgeBaseMetadataField] | None = None,
524        embedding_model: str | None = None,
525        chat_model: AiChatModelSelection | None = None,
526        name: str | None = None,
527        vector_db_type: VectorDbType | None = None,
528        graph_rag: AiKnowledgeBaseGraphConfig | None = None,
529    ) -> None:
530        """Create or update the knowledge base.
531
532        Args:
533            description: Description of the knowledge base's content.
534            metadata_fields: Schema for metadata fields used in filtering.
535                Each field: ``{'name': str, 'dataType': str, 'required': bool, 'description'?: str}``.
536            embedding_model: The embedding model name for vectorization.
537                Required when creating a new knowledge base.
538            chat_model: The LLM model for answering questions over this KB.
539            name: Display name for the knowledge base.
540            vector_db_type: The vector store backend. Set at creation (defaulting to the
541                server's default) and immutable thereafter. The knowledge graph requires
542                ``'mongoAtlas'``.
543            graph_rag: Opt-in GraphRAG configuration (``{'enabled': True, ...}``). Only
544                honored on ``'mongoAtlas'`` knowledge bases; mutable, unlike
545                ``vector_db_type``. The value replaces the stored config rather than
546                merging into it. See :class:`AiKnowledgeBaseGraphConfig`.
547        """
548        kb: dict[str, Any] = {"id": self._kb_id}
549        if description is not None:
550            kb["description"] = description
551        if metadata_fields is not None:
552            kb["metadataFields"] = metadata_fields
553        if embedding_model is not None:
554            kb["embeddingModel"] = embedding_model
555        if chat_model is not None:
556            kb["chatModel"] = chat_model
557        if name is not None:
558            kb["name"] = name
559        if vector_db_type is not None:
560            kb["vectorDbType"] = vector_db_type
561        if graph_rag is not None:
562            kb["graphRag"] = graph_rag
563        await self._http.post("squid-api/v1/ai/knowledge-base/upsert", {"knowledgeBase": kb})
564
565    async def delete(self) -> None:
566        """Delete the knowledge base and all its contexts permanently."""
567        await self._http.post("squid-api/v1/ai/knowledge-base/delete", {"id": self._kb_id})
568
569    # --- Contexts ---
570
571    async def get_context(
572        self, context_id: str, *, truncate_text_after: int | None = None
573    ) -> dict | None:
574        """Get a specific context entry.
575
576        Args:
577            context_id: The unique context identifier.
578            truncate_text_after: The number of characters after which the context's
579                text is truncated. Pass ``0`` to return an empty text.
580
581        Returns:
582            An ``AiKnowledgeBaseContext`` dict with keys: ``id``, ``appId``,
583            ``knowledgeBaseId``, ``createdAt``, ``updatedAt``, ``type``,
584            ``title``, ``text``, ``preview``, ``sizeBytes``, ``metadata``,
585            ``requestConfig``. Returns ``None`` if not found.
586        """
587        params = (
588            {"truncateTextAfter": str(truncate_text_after)}
589            if truncate_text_after is not None
590            else None
591        )
592        return await self._http.get(
593            f"squid-api/v1/ai/knowledge-base/getContext/{self._kb_id}/{context_id}",
594            params=params,
595        )
596
597    async def list_contexts(self, *, truncate_text_after: int | None = None) -> list[dict]:
598        """List all contexts in the knowledge base.
599
600        Deprecated: fetches every context in one call with no pagination — expensive for large
601        knowledge bases. Use :meth:`list_contexts_page` instead, which supports
602        ``offset``/``limit``/``search``.
603
604        Args:
605            truncate_text_after: The number of characters after which each context's
606                text is truncated.
607
608        Returns:
609            A list of ``AiKnowledgeBaseContext`` dicts.
610        """
611        params = (
612            {"truncateTextAfter": str(truncate_text_after)}
613            if truncate_text_after is not None
614            else None
615        )
616        result = await self._http.get(
617            f"squid-api/v1/ai/knowledge-base/listContexts/{self._kb_id}", params=params
618        )
619        return result.get("contexts", []) if result else []
620
621    async def list_contexts_page(
622        self,
623        *,
624        offset: int | None = None,
625        limit: int | None = None,
626        search: str | None = None,
627        truncate_text_after: int | None = None,
628    ) -> dict:
629        """List a page of contexts in the knowledge base.
630
631        Args:
632            offset: The number of contexts to skip, for pagination.
633            limit: The maximum number of contexts to return, for pagination.
634            search: Case-insensitive substring search across id/title only.
635            truncate_text_after: The number of characters after which each context's
636                text is truncated.
637
638        Returns:
639            A dict with ``contexts`` (a list of ``AiKnowledgeBaseContext`` dicts for the
640            requested page) and ``totalCount`` (the total number of contexts in the
641            knowledge base, ignoring ``offset``/``limit``).
642        """
643        params = {
644            key: str(value)
645            for key, value in {
646                "offset": offset,
647                "limit": limit,
648                "search": search,
649                "truncateTextAfter": truncate_text_after,
650            }.items()
651            if value is not None
652        }
653        result = await self._http.get(
654            f"squid-api/v1/ai/knowledge-base/listContextsPage/{self._kb_id}",
655            params=params or None,
656        )
657        return result if result else {"contexts": [], "totalCount": 0}
658
659    async def upsert_contexts(
660        self,
661        contexts: list[ContextRequest],
662        files: list[tuple[str, bytes, str]] | None = None,
663    ) -> dict:
664        """Add or update contexts in the knowledge base.
665
666        Args:
667            contexts: A list of context request objects. Each must be either:
668                - A :class:`TextContextRequest`: ``{'contextId', 'type': 'text', 'title', 'text', ...}``
669                - A :class:`FileContextRequest`: ``{'contextId', 'type': 'file', ...}``
670            files: For file contexts, provide the actual file data as a list of
671                ``(filename, data_bytes, content_type)`` tuples. Must match the
672                order of file contexts in the ``contexts`` list.
673
674        Returns:
675            A dict with ``failures``: a list of ``UpsertContextStatusError`` dicts
676            for any contexts that failed to upsert.
677
678        Example::
679
680            await kb.upsert_contexts(
681                [
682                    {"contextId": "doc-1", "type": "text", "title": "FAQ", "text": "..."},
683                    {"contextId": "doc-2", "type": "file"},
684                ],
685                files=[
686                    ("manual.pdf", pdf_bytes, "application/pdf"),
687                ],
688            )
689        """
690        form_data = {
691            "knowledgeBaseId": self._kb_id,
692            "contexts": json.dumps(contexts),
693        }
694        file_tuples: list[tuple[str, tuple[str, bytes, str]]] = []
695        if files:
696            for fname, fdata, ftype in files:
697                file_tuples.append(("files", (fname, fdata, ftype)))
698        return await self._http.post_form(
699            "squid-api/v1/ai/knowledge-base/upsertContexts",
700            data=form_data,
701            files=file_tuples,
702        )
703
704    async def delete_contexts(self, context_ids: list[str]) -> None:
705        """Delete contexts by their IDs.
706
707        Args:
708            context_ids: List of context IDs to delete.
709        """
710        await self._http.post(
711            "squid-api/v1/ai/knowledge-base/deleteContexts",
712            {"knowledgeBaseId": self._kb_id, "contextIds": context_ids},
713        )
714
715    # --- Search ---
716
717    async def search(
718        self,
719        prompt: str,
720        options: KnowledgeBaseSearchOptions | None = None,
721    ) -> list[dict]:
722        """Search the knowledge base using semantic search.
723
724        Args:
725            prompt: The search query in natural language.
726            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
727                Supports: ``limit``, ``chunkLimit``, ``rerankProvider``, ``rerankScoreThreshold``,
728                ``chatModel``, ``searchMode``, ``graphOptions``, ``graphFilter``.
729
730        Returns:
731            A list of ``AiKnowledgeBaseSearchResultChunk`` dicts, each with:
732            ``contextId``, ``data``, ``metadata``, ``score``.
733
734        Example::
735
736            chunks = await kb.search(
737                "How do I reset my password?",
738                options={
739                    "limit": 5,
740                    "chatModel": "gemini-3.8-flash",
741                },
742            )
743            for chunk in chunks:
744                print(f"Score: {chunk['score']}, Data: {chunk['data'][:100]}")
745        """
746        response = await self.search_with_graph_context(prompt, options)
747        return response.get("chunks", [])
748
749    # --- Knowledge graph ---
750
751    async def search_with_graph_context(
752        self,
753        prompt: str,
754        options: KnowledgeBaseSearchOptions | None = None,
755    ) -> dict:
756        """Like :meth:`search`, but return the full search response instead of only the chunks.
757
758        With ``searchMode: 'graph'`` and ``graphOptions: {'includeGraphContext': True}`` the
759        response also carries ``graphContext``, the subgraph the search traversed.
760
761        Args:
762            prompt: The search query in natural language.
763            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
764
765        Returns:
766            An ``AiKnowledgeBaseSearchResponse`` dict: ``chunks`` (list of result chunk dicts)
767            and, when requested via ``includeGraphContext``, ``graphContext``
768            (``{'entities': [...], 'relationships': [...]}``).
769        """
770        search_options: dict[str, Any] = {"prompt": prompt, **(options or {})}
771        result = await self._http.post(
772            "squid-api/v1/ai/knowledge-base/search",
773            {
774                "knowledgeBaseId": self._kb_id,
775                "prompt": prompt,
776                "options": search_options,
777            },
778        )
779        return result or {"chunks": []}
780
781    async def get_graph_status(self, *, include_topics: bool | None = None) -> dict:
782        """Get the knowledge base's graph build/readiness status.
783
784        Args:
785            include_topics: Whether to include the topic table (``topics``, ``topicEdges``).
786                Default True; pass False to poll the build state without it.
787
788        Returns:
789            A ``GetKnowledgeBaseGraphStatusResponse`` dict with ``enabled``, ``entityCount``,
790            ``relationshipCount``, ``contextsIndexed``, ``contextsTotal``, and, where available,
791            ``facets``, ``topics``, ``structureStale``, LLM usage fields, and ``buildJob``
792            (``{'status': 'in_progress' | 'completed' | 'failed', ...}``) for the most recent
793            rebuild. Poll it after :meth:`rebuild_graph` until ``buildJob`` leaves
794            ``'in_progress'``.
795        """
796        params: dict[str, str] | None = None
797        if include_topics is not None:
798            params = {"includeTopics": "true" if include_topics else "false"}
799        return await self._http.get(
800            f"squid-api/v1/ai/knowledge-base/getGraphStatus/{self._kb_id}", params=params
801        )
802
803    async def get_file_status(self, path: str) -> dict:
804        """Look up the durable ingestion status of one file by path.
805
806        Args:
807            path: The file's path: ``folderPath/filename``, or just ``filename`` at the
808                upload root.
809
810        Returns:
811            An ``AiKnowledgeBaseFileStatusInfo`` dict: ``status`` is one of ``'completed'``,
812            ``'failed'`` (with ``errorMessage``/``errorCode``), ``'in_progress'``, or
813            ``'no_record'`` when the knowledge base has no trace of that path.
814        """
815        return await self._http.get(
816            f"squid-api/v1/ai/knowledge-base/getFileStatus/{self._kb_id}",
817            params={"path": path},
818        )
819
820    async def get_folder_status(self, folder_path: str, *, file_limit: int | None = None) -> dict:
821        """Search and aggregate the durable ingestion status of every file matching ``folder_path``.
822
823        Read three ways and unioned: a folder subtree (matched the same segment-safe way as
824        ``$underPath`` metadata filters), a bare filename anywhere in the knowledge base, and,
825        when it contains a ``/``, an exact ``folder_path/filename`` path.
826
827        Args:
828            folder_path: The folder subtree, bare filename, or exact file path to search for.
829            file_limit: Maximum number of files to return in ``files``, prioritizing failures.
830                Defaults to 50.
831
832        Returns:
833            An ``AiKnowledgeBaseFolderStatusResponse`` dict with ``counts`` (per-status totals),
834            ``totalFiles``, ``hasAnyRecords``, ``files`` (capped, failures first), and
835            ``truncated``.
836        """
837        params = {
838            key: str(value)
839            for key, value in {"folderPath": folder_path, "fileLimit": file_limit}.items()
840            if value is not None
841        }
842        return await self._http.get(
843            f"squid-api/v1/ai/knowledge-base/getFolderStatus/{self._kb_id}",
844            params=params,
845        )
846
847    async def rebuild_graph(
848        self,
849        *,
850        mode: Literal["full", "structural"] | None = None,
851        ignore_active_bulk_jobs: bool | None = None,
852    ) -> None:
853        """Enqueue a graph rebuild/backfill. Requires ``graphRag.enabled``.
854
855        Only one rebuild can run per knowledge base at a time; track progress via
856        :meth:`get_graph_status`. A rebuild while a bulk-ingestion job on the knowledge base is
857        still active is refused (``GRAPH_REBUILD_BLOCKED_BY_BULK_JOB``).
858
859        Args:
860            mode: ``'structural'`` (the default) keeps the already-extracted entity graph and
861                rebuilds just the concept layer; ``'full'`` wipes the graph and re-extracts every
862                context with an LLM — expensive, and only needed when the extraction itself must
863                be redone (e.g. after changing ``entityTypes`` or ``extractionModel``).
864            ignore_active_bulk_jobs: start the rebuild even while a bulk-ingestion job is active.
865                For a wedged job only: the rebuild re-extracts what a live job is still merging.
866        """
867        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
868        if mode is not None:
869            request["mode"] = mode
870        if ignore_active_bulk_jobs is not None:
871            request["ignoreActiveBulkJobs"] = ignore_active_bulk_jobs
872        await self._http.post("squid-api/v1/ai/knowledge-base/rebuildGraph", request)
873
874    async def query_graph(
875        self,
876        op: KnowledgeBaseGraphQueryOp,
877        *,
878        ref: str | None = None,
879        ref_b: str | None = None,
880        context_id: str | None = None,
881        query: str | None = None,
882        depth: int | None = None,
883        hops: int | None = None,
884        recursive: bool | None = None,
885        limit: int | None = None,
886        context_metadata_filter: dict[str, Any] | None = None,
887    ) -> dict:
888        """Query the knowledge base's graph (documents, entities, themes, facets).
889
890        Requires ``graphRag.enabled`` on the knowledge base.
891
892        Args:
893            op: The operation to run.
894            ref: Node ref (name, facet nodeId, or a topic or entity id from
895                :meth:`explore_graph`). Required by resolve/describe/subtree/docsUnder/
896                neighborhood/pathBetween/relationship.
897            ref_b: Second node ref for ``pathBetween``, and the target entity for ``relationship``.
898            context_id: The document's contextId for ``conceptsOf``.
899            query: The free-text question for ``globalSummary``.
900            depth: ``subtree``: maximum depth below the resolved node. Default (and cap) 3.
901            hops: ``neighborhood``: expansion hops from the resolved entity. Default 1, max 2.
902            recursive: ``docsUnder``: when True (the default), include documents under the whole
903                subtree, not just the node itself.
904            limit: ``docsUnder`` and ``relationship``: maximum documents returned. Default 25, max 100.
905            context_metadata_filter: ``docsUnder`` and ``relationship``: keeps only the documents
906                whose context matches this metadata filter (context-record metadata only).
907
908        Returns:
909            A ``QueryKnowledgeBaseGraphResponse`` dict; which keys are present depends on ``op``
910            (e.g. ``overview`` for 'overview', ``matches`` for 'resolve', ``node``/``path``/
911            ``related`` for 'describe', ``tree``, ``documents``/``totalDocs``, ``concepts``,
912            ``graph``, ``pathBetween``, ``summaries``, ``relationship``).
913        """
914        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id, "op": op}
915        if ref is not None:
916            request["ref"] = ref
917        if ref_b is not None:
918            request["refB"] = ref_b
919        if context_id is not None:
920            request["contextId"] = context_id
921        if query is not None:
922            request["query"] = query
923        if depth is not None:
924            request["depth"] = depth
925        if hops is not None:
926            request["hops"] = hops
927        if recursive is not None:
928            request["recursive"] = recursive
929        if limit is not None:
930            request["limit"] = limit
931        if context_metadata_filter is not None:
932            request["contextMetadataFilter"] = context_metadata_filter
933        return await self._http.post("squid-api/v1/ai/knowledge-base/queryGraph", request)
934
935    async def explore_graph(
936        self,
937        *,
938        node_limit: int | None = None,
939        topic_id: str | None = None,
940        entity_id: str | None = None,
941        search: str | None = None,
942        context_metadata_filter: dict[str, Any] | None = None,
943        include_topics: bool | None = None,
944    ) -> dict:
945        """Get a bounded slice of the knowledge base's entity graph for exploration/visualization.
946
947        By default returns the highest-degree entities and the relationships among them. Requires
948        ``graphRag.enabled``. ``topic_id``, ``entity_id`` and ``search`` select other entity pools;
949        pass at most one of them.
950
951        Args:
952            node_limit: Hard cap on returned nodes. Default 200, max 1000.
953            topic_id: Restricts the subgraph to entities under the given topic node id. Ids come
954                from :meth:`get_graph_status`'s ``topics`` and churn on rebuild.
955            entity_id: Centers the subgraph on one entity: the entity plus its neighbors, strongest
956                relationships first. Ids come from the returned nodes and churn on rebuild.
957            search: Finds entities by name (exact, then prefix, then substring); when no name
958                matches, returns the entities nearest in meaning.
959            context_metadata_filter: Keeps only the entities mentioned by contexts matching this
960                metadata filter (context-record metadata only).
961            include_topics: Whether to attach the topic table (``topics``). Default True; pass
962                False when the caller already holds it.
963
964        Returns:
965            An ``ExploreKnowledgeBaseGraphResponse`` dict: ``nodes``, ``edges`` (the heaviest,
966            capped; ``edgesTruncated`` when cut), ``totalCount`` when counted, ``searchMatch`` for
967            a search, ``scopeTruncated`` when the filter matched too many contexts, and, once the
968            concept layer has been built, ``topics``.
969        """
970        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
971        if node_limit is not None:
972            request["nodeLimit"] = node_limit
973        if topic_id is not None:
974            request["topicId"] = topic_id
975        if entity_id is not None:
976            request["entityId"] = entity_id
977        if search is not None:
978            request["search"] = search
979        if context_metadata_filter is not None:
980            request["contextMetadataFilter"] = context_metadata_filter
981        if include_topics is not None:
982            request["includeTopics"] = include_topics
983        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)
505    def __init__(self, http: HttpTransport, kb_id: str) -> None:
506        self._http = http
507        self._kb_id = kb_id
async def get(self) -> dict | None:
509    async def get(self) -> dict | None:
510        """Get the knowledge base details.
511
512        Returns:
513            An ``AiKnowledgeBase`` dict with keys: ``id``, ``appId``,
514            ``description``, ``metadataFields``, ``embeddingModel``,
515            ``chatModel``, ``updatedAt``. Returns ``None`` if not found.
516        """
517        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:
519    async def upsert(
520        self,
521        *,
522        description: str | None = None,
523        metadata_fields: list[AiKnowledgeBaseMetadataField] | None = None,
524        embedding_model: str | None = None,
525        chat_model: AiChatModelSelection | None = None,
526        name: str | None = None,
527        vector_db_type: VectorDbType | None = None,
528        graph_rag: AiKnowledgeBaseGraphConfig | None = None,
529    ) -> None:
530        """Create or update the knowledge base.
531
532        Args:
533            description: Description of the knowledge base's content.
534            metadata_fields: Schema for metadata fields used in filtering.
535                Each field: ``{'name': str, 'dataType': str, 'required': bool, 'description'?: str}``.
536            embedding_model: The embedding model name for vectorization.
537                Required when creating a new knowledge base.
538            chat_model: The LLM model for answering questions over this KB.
539            name: Display name for the knowledge base.
540            vector_db_type: The vector store backend. Set at creation (defaulting to the
541                server's default) and immutable thereafter. The knowledge graph requires
542                ``'mongoAtlas'``.
543            graph_rag: Opt-in GraphRAG configuration (``{'enabled': True, ...}``). Only
544                honored on ``'mongoAtlas'`` knowledge bases; mutable, unlike
545                ``vector_db_type``. The value replaces the stored config rather than
546                merging into it. See :class:`AiKnowledgeBaseGraphConfig`.
547        """
548        kb: dict[str, Any] = {"id": self._kb_id}
549        if description is not None:
550            kb["description"] = description
551        if metadata_fields is not None:
552            kb["metadataFields"] = metadata_fields
553        if embedding_model is not None:
554            kb["embeddingModel"] = embedding_model
555        if chat_model is not None:
556            kb["chatModel"] = chat_model
557        if name is not None:
558            kb["name"] = name
559        if vector_db_type is not None:
560            kb["vectorDbType"] = vector_db_type
561        if graph_rag is not None:
562            kb["graphRag"] = graph_rag
563        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:
565    async def delete(self) -> None:
566        """Delete the knowledge base and all its contexts permanently."""
567        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, *, truncate_text_after: int | None = None) -> dict | None:
571    async def get_context(
572        self, context_id: str, *, truncate_text_after: int | None = None
573    ) -> dict | None:
574        """Get a specific context entry.
575
576        Args:
577            context_id: The unique context identifier.
578            truncate_text_after: The number of characters after which the context's
579                text is truncated. Pass ``0`` to return an empty text.
580
581        Returns:
582            An ``AiKnowledgeBaseContext`` dict with keys: ``id``, ``appId``,
583            ``knowledgeBaseId``, ``createdAt``, ``updatedAt``, ``type``,
584            ``title``, ``text``, ``preview``, ``sizeBytes``, ``metadata``,
585            ``requestConfig``. Returns ``None`` if not found.
586        """
587        params = (
588            {"truncateTextAfter": str(truncate_text_after)}
589            if truncate_text_after is not None
590            else None
591        )
592        return await self._http.get(
593            f"squid-api/v1/ai/knowledge-base/getContext/{self._kb_id}/{context_id}",
594            params=params,
595        )

Get a specific context entry.

Arguments:
  • context_id: The unique context identifier.
  • truncate_text_after: The number of characters after which the context's text is truncated. Pass 0 to return an empty text.
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, *, truncate_text_after: int | None = None) -> list[dict]:
597    async def list_contexts(self, *, truncate_text_after: int | None = None) -> list[dict]:
598        """List all contexts in the knowledge base.
599
600        Deprecated: fetches every context in one call with no pagination — expensive for large
601        knowledge bases. Use :meth:`list_contexts_page` instead, which supports
602        ``offset``/``limit``/``search``.
603
604        Args:
605            truncate_text_after: The number of characters after which each context's
606                text is truncated.
607
608        Returns:
609            A list of ``AiKnowledgeBaseContext`` dicts.
610        """
611        params = (
612            {"truncateTextAfter": str(truncate_text_after)}
613            if truncate_text_after is not None
614            else None
615        )
616        result = await self._http.get(
617            f"squid-api/v1/ai/knowledge-base/listContexts/{self._kb_id}", params=params
618        )
619        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.

Arguments:
  • truncate_text_after: The number of characters after which each context's text is truncated.
Returns:

A list of AiKnowledgeBaseContext dicts.

async def list_contexts_page( self, *, offset: int | None = None, limit: int | None = None, search: str | None = None, truncate_text_after: int | None = None) -> dict:
621    async def list_contexts_page(
622        self,
623        *,
624        offset: int | None = None,
625        limit: int | None = None,
626        search: str | None = None,
627        truncate_text_after: int | None = None,
628    ) -> dict:
629        """List a page of contexts in the knowledge base.
630
631        Args:
632            offset: The number of contexts to skip, for pagination.
633            limit: The maximum number of contexts to return, for pagination.
634            search: Case-insensitive substring search across id/title only.
635            truncate_text_after: The number of characters after which each context's
636                text is truncated.
637
638        Returns:
639            A dict with ``contexts`` (a list of ``AiKnowledgeBaseContext`` dicts for the
640            requested page) and ``totalCount`` (the total number of contexts in the
641            knowledge base, ignoring ``offset``/``limit``).
642        """
643        params = {
644            key: str(value)
645            for key, value in {
646                "offset": offset,
647                "limit": limit,
648                "search": search,
649                "truncateTextAfter": truncate_text_after,
650            }.items()
651            if value is not None
652        }
653        result = await self._http.get(
654            f"squid-api/v1/ai/knowledge-base/listContextsPage/{self._kb_id}",
655            params=params or None,
656        )
657        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.
  • truncate_text_after: The number of characters after which each context's text is truncated.
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:
659    async def upsert_contexts(
660        self,
661        contexts: list[ContextRequest],
662        files: list[tuple[str, bytes, str]] | None = None,
663    ) -> dict:
664        """Add or update contexts in the knowledge base.
665
666        Args:
667            contexts: A list of context request objects. Each must be either:
668                - A :class:`TextContextRequest`: ``{'contextId', 'type': 'text', 'title', 'text', ...}``
669                - A :class:`FileContextRequest`: ``{'contextId', 'type': 'file', ...}``
670            files: For file contexts, provide the actual file data as a list of
671                ``(filename, data_bytes, content_type)`` tuples. Must match the
672                order of file contexts in the ``contexts`` list.
673
674        Returns:
675            A dict with ``failures``: a list of ``UpsertContextStatusError`` dicts
676            for any contexts that failed to upsert.
677
678        Example::
679
680            await kb.upsert_contexts(
681                [
682                    {"contextId": "doc-1", "type": "text", "title": "FAQ", "text": "..."},
683                    {"contextId": "doc-2", "type": "file"},
684                ],
685                files=[
686                    ("manual.pdf", pdf_bytes, "application/pdf"),
687                ],
688            )
689        """
690        form_data = {
691            "knowledgeBaseId": self._kb_id,
692            "contexts": json.dumps(contexts),
693        }
694        file_tuples: list[tuple[str, tuple[str, bytes, str]]] = []
695        if files:
696            for fname, fdata, ftype in files:
697                file_tuples.append(("files", (fname, fdata, ftype)))
698        return await self._http.post_form(
699            "squid-api/v1/ai/knowledge-base/upsertContexts",
700            data=form_data,
701            files=file_tuples,
702        )

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:
704    async def delete_contexts(self, context_ids: list[str]) -> None:
705        """Delete contexts by their IDs.
706
707        Args:
708            context_ids: List of context IDs to delete.
709        """
710        await self._http.post(
711            "squid-api/v1/ai/knowledge-base/deleteContexts",
712            {"knowledgeBaseId": self._kb_id, "contextIds": context_ids},
713        )

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]:
717    async def search(
718        self,
719        prompt: str,
720        options: KnowledgeBaseSearchOptions | None = None,
721    ) -> list[dict]:
722        """Search the knowledge base using semantic search.
723
724        Args:
725            prompt: The search query in natural language.
726            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
727                Supports: ``limit``, ``chunkLimit``, ``rerankProvider``, ``rerankScoreThreshold``,
728                ``chatModel``, ``searchMode``, ``graphOptions``, ``graphFilter``.
729
730        Returns:
731            A list of ``AiKnowledgeBaseSearchResultChunk`` dicts, each with:
732            ``contextId``, ``data``, ``metadata``, ``score``.
733
734        Example::
735
736            chunks = await kb.search(
737                "How do I reset my password?",
738                options={
739                    "limit": 5,
740                    "chatModel": "gemini-3.8-flash",
741                },
742            )
743            for chunk in chunks:
744                print(f"Score: {chunk['score']}, Data: {chunk['data'][:100]}")
745        """
746        response = await self.search_with_graph_context(prompt, options)
747        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:
751    async def search_with_graph_context(
752        self,
753        prompt: str,
754        options: KnowledgeBaseSearchOptions | None = None,
755    ) -> dict:
756        """Like :meth:`search`, but return the full search response instead of only the chunks.
757
758        With ``searchMode: 'graph'`` and ``graphOptions: {'includeGraphContext': True}`` the
759        response also carries ``graphContext``, the subgraph the search traversed.
760
761        Args:
762            prompt: The search query in natural language.
763            options: Search options. See :class:`KnowledgeBaseSearchOptions`.
764
765        Returns:
766            An ``AiKnowledgeBaseSearchResponse`` dict: ``chunks`` (list of result chunk dicts)
767            and, when requested via ``includeGraphContext``, ``graphContext``
768            (``{'entities': [...], 'relationships': [...]}``).
769        """
770        search_options: dict[str, Any] = {"prompt": prompt, **(options or {})}
771        result = await self._http.post(
772            "squid-api/v1/ai/knowledge-base/search",
773            {
774                "knowledgeBaseId": self._kb_id,
775                "prompt": prompt,
776                "options": search_options,
777            },
778        )
779        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, *, include_topics: bool | None = None) -> dict:
781    async def get_graph_status(self, *, include_topics: bool | None = None) -> dict:
782        """Get the knowledge base's graph build/readiness status.
783
784        Args:
785            include_topics: Whether to include the topic table (``topics``, ``topicEdges``).
786                Default True; pass False to poll the build state without it.
787
788        Returns:
789            A ``GetKnowledgeBaseGraphStatusResponse`` dict with ``enabled``, ``entityCount``,
790            ``relationshipCount``, ``contextsIndexed``, ``contextsTotal``, and, where available,
791            ``facets``, ``topics``, ``structureStale``, LLM usage fields, and ``buildJob``
792            (``{'status': 'in_progress' | 'completed' | 'failed', ...}``) for the most recent
793            rebuild. Poll it after :meth:`rebuild_graph` until ``buildJob`` leaves
794            ``'in_progress'``.
795        """
796        params: dict[str, str] | None = None
797        if include_topics is not None:
798            params = {"includeTopics": "true" if include_topics else "false"}
799        return await self._http.get(
800            f"squid-api/v1/ai/knowledge-base/getGraphStatus/{self._kb_id}", params=params
801        )

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

Arguments:
  • include_topics: Whether to include the topic table (topics, topicEdges). Default True; pass False to poll the build state without it.
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 get_file_status(self, path: str) -> dict:
803    async def get_file_status(self, path: str) -> dict:
804        """Look up the durable ingestion status of one file by path.
805
806        Args:
807            path: The file's path: ``folderPath/filename``, or just ``filename`` at the
808                upload root.
809
810        Returns:
811            An ``AiKnowledgeBaseFileStatusInfo`` dict: ``status`` is one of ``'completed'``,
812            ``'failed'`` (with ``errorMessage``/``errorCode``), ``'in_progress'``, or
813            ``'no_record'`` when the knowledge base has no trace of that path.
814        """
815        return await self._http.get(
816            f"squid-api/v1/ai/knowledge-base/getFileStatus/{self._kb_id}",
817            params={"path": path},
818        )

Look up the durable ingestion status of one file by path.

Arguments:
  • path: The file's path: folderPath/filename, or just filename at the upload root.
Returns:

An AiKnowledgeBaseFileStatusInfo dict: status is one of 'completed', 'failed' (with errorMessage/errorCode), 'in_progress', or 'no_record' when the knowledge base has no trace of that path.

async def get_folder_status(self, folder_path: str, *, file_limit: int | None = None) -> dict:
820    async def get_folder_status(self, folder_path: str, *, file_limit: int | None = None) -> dict:
821        """Search and aggregate the durable ingestion status of every file matching ``folder_path``.
822
823        Read three ways and unioned: a folder subtree (matched the same segment-safe way as
824        ``$underPath`` metadata filters), a bare filename anywhere in the knowledge base, and,
825        when it contains a ``/``, an exact ``folder_path/filename`` path.
826
827        Args:
828            folder_path: The folder subtree, bare filename, or exact file path to search for.
829            file_limit: Maximum number of files to return in ``files``, prioritizing failures.
830                Defaults to 50.
831
832        Returns:
833            An ``AiKnowledgeBaseFolderStatusResponse`` dict with ``counts`` (per-status totals),
834            ``totalFiles``, ``hasAnyRecords``, ``files`` (capped, failures first), and
835            ``truncated``.
836        """
837        params = {
838            key: str(value)
839            for key, value in {"folderPath": folder_path, "fileLimit": file_limit}.items()
840            if value is not None
841        }
842        return await self._http.get(
843            f"squid-api/v1/ai/knowledge-base/getFolderStatus/{self._kb_id}",
844            params=params,
845        )

Search and aggregate the durable ingestion status of every file matching folder_path.

Read three ways and unioned: a folder subtree (matched the same segment-safe way as $underPath metadata filters), a bare filename anywhere in the knowledge base, and, when it contains a /, an exact folder_path/filename path.

Arguments:
  • folder_path: The folder subtree, bare filename, or exact file path to search for.
  • file_limit: Maximum number of files to return in files, prioritizing failures. Defaults to 50.
Returns:

An AiKnowledgeBaseFolderStatusResponse dict with counts (per-status totals), totalFiles, hasAnyRecords, files (capped, failures first), and truncated.

async def rebuild_graph( self, *, mode: Literal['full', 'structural'] | None = None, ignore_active_bulk_jobs: bool | None = None) -> None:
847    async def rebuild_graph(
848        self,
849        *,
850        mode: Literal["full", "structural"] | None = None,
851        ignore_active_bulk_jobs: bool | None = None,
852    ) -> None:
853        """Enqueue a graph rebuild/backfill. Requires ``graphRag.enabled``.
854
855        Only one rebuild can run per knowledge base at a time; track progress via
856        :meth:`get_graph_status`. A rebuild while a bulk-ingestion job on the knowledge base is
857        still active is refused (``GRAPH_REBUILD_BLOCKED_BY_BULK_JOB``).
858
859        Args:
860            mode: ``'structural'`` (the default) keeps the already-extracted entity graph and
861                rebuilds just the concept layer; ``'full'`` wipes the graph and re-extracts every
862                context with an LLM — expensive, and only needed when the extraction itself must
863                be redone (e.g. after changing ``entityTypes`` or ``extractionModel``).
864            ignore_active_bulk_jobs: start the rebuild even while a bulk-ingestion job is active.
865                For a wedged job only: the rebuild re-extracts what a live job is still merging.
866        """
867        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
868        if mode is not None:
869            request["mode"] = mode
870        if ignore_active_bulk_jobs is not None:
871            request["ignoreActiveBulkJobs"] = ignore_active_bulk_jobs
872        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', 'relationship'], *, 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, context_metadata_filter: dict[str, Any] | None = None) -> dict:
874    async def query_graph(
875        self,
876        op: KnowledgeBaseGraphQueryOp,
877        *,
878        ref: str | None = None,
879        ref_b: str | None = None,
880        context_id: str | None = None,
881        query: str | None = None,
882        depth: int | None = None,
883        hops: int | None = None,
884        recursive: bool | None = None,
885        limit: int | None = None,
886        context_metadata_filter: dict[str, Any] | None = None,
887    ) -> dict:
888        """Query the knowledge base's graph (documents, entities, themes, facets).
889
890        Requires ``graphRag.enabled`` on the knowledge base.
891
892        Args:
893            op: The operation to run.
894            ref: Node ref (name, facet nodeId, or a topic or entity id from
895                :meth:`explore_graph`). Required by resolve/describe/subtree/docsUnder/
896                neighborhood/pathBetween/relationship.
897            ref_b: Second node ref for ``pathBetween``, and the target entity for ``relationship``.
898            context_id: The document's contextId for ``conceptsOf``.
899            query: The free-text question for ``globalSummary``.
900            depth: ``subtree``: maximum depth below the resolved node. Default (and cap) 3.
901            hops: ``neighborhood``: expansion hops from the resolved entity. Default 1, max 2.
902            recursive: ``docsUnder``: when True (the default), include documents under the whole
903                subtree, not just the node itself.
904            limit: ``docsUnder`` and ``relationship``: maximum documents returned. Default 25, max 100.
905            context_metadata_filter: ``docsUnder`` and ``relationship``: keeps only the documents
906                whose context matches this metadata filter (context-record metadata only).
907
908        Returns:
909            A ``QueryKnowledgeBaseGraphResponse`` dict; which keys are present depends on ``op``
910            (e.g. ``overview`` for 'overview', ``matches`` for 'resolve', ``node``/``path``/
911            ``related`` for 'describe', ``tree``, ``documents``/``totalDocs``, ``concepts``,
912            ``graph``, ``pathBetween``, ``summaries``, ``relationship``).
913        """
914        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id, "op": op}
915        if ref is not None:
916            request["ref"] = ref
917        if ref_b is not None:
918            request["refB"] = ref_b
919        if context_id is not None:
920            request["contextId"] = context_id
921        if query is not None:
922            request["query"] = query
923        if depth is not None:
924            request["depth"] = depth
925        if hops is not None:
926            request["hops"] = hops
927        if recursive is not None:
928            request["recursive"] = recursive
929        if limit is not None:
930            request["limit"] = limit
931        if context_metadata_filter is not None:
932            request["contextMetadataFilter"] = context_metadata_filter
933        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, facet nodeId, or a topic or entity id from explore_graph()). Required by resolve/describe/subtree/docsUnder/ neighborhood/pathBetween/relationship.
  • ref_b: Second node ref for pathBetween, and the target entity for relationship.
  • 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 and relationship: maximum documents returned. Default 25, max 100.
  • context_metadata_filter: docsUnder and relationship: keeps only the documents whose context matches this metadata filter (context-record metadata only).
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, relationship).

async def explore_graph( self, *, node_limit: int | None = None, topic_id: str | None = None, entity_id: str | None = None, search: str | None = None, context_metadata_filter: dict[str, Any] | None = None, include_topics: bool | None = None) -> dict:
935    async def explore_graph(
936        self,
937        *,
938        node_limit: int | None = None,
939        topic_id: str | None = None,
940        entity_id: str | None = None,
941        search: str | None = None,
942        context_metadata_filter: dict[str, Any] | None = None,
943        include_topics: bool | None = None,
944    ) -> dict:
945        """Get a bounded slice of the knowledge base's entity graph for exploration/visualization.
946
947        By default returns the highest-degree entities and the relationships among them. Requires
948        ``graphRag.enabled``. ``topic_id``, ``entity_id`` and ``search`` select other entity pools;
949        pass at most one of them.
950
951        Args:
952            node_limit: Hard cap on returned nodes. Default 200, max 1000.
953            topic_id: Restricts the subgraph to entities under the given topic node id. Ids come
954                from :meth:`get_graph_status`'s ``topics`` and churn on rebuild.
955            entity_id: Centers the subgraph on one entity: the entity plus its neighbors, strongest
956                relationships first. Ids come from the returned nodes and churn on rebuild.
957            search: Finds entities by name (exact, then prefix, then substring); when no name
958                matches, returns the entities nearest in meaning.
959            context_metadata_filter: Keeps only the entities mentioned by contexts matching this
960                metadata filter (context-record metadata only).
961            include_topics: Whether to attach the topic table (``topics``). Default True; pass
962                False when the caller already holds it.
963
964        Returns:
965            An ``ExploreKnowledgeBaseGraphResponse`` dict: ``nodes``, ``edges`` (the heaviest,
966            capped; ``edgesTruncated`` when cut), ``totalCount`` when counted, ``searchMatch`` for
967            a search, ``scopeTruncated`` when the filter matched too many contexts, and, once the
968            concept layer has been built, ``topics``.
969        """
970        request: dict[str, Any] = {"knowledgeBaseId": self._kb_id}
971        if node_limit is not None:
972            request["nodeLimit"] = node_limit
973        if topic_id is not None:
974            request["topicId"] = topic_id
975        if entity_id is not None:
976            request["entityId"] = entity_id
977        if search is not None:
978            request["search"] = search
979        if context_metadata_filter is not None:
980            request["contextMetadataFilter"] = context_metadata_filter
981        if include_topics is not None:
982            request["includeTopics"] = include_topics
983        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.

By default returns the highest-degree entities and the relationships among them. Requires graphRag.enabled. topic_id, entity_id and search select other entity pools; pass at most one of them.

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.
  • entity_id: Centers the subgraph on one entity: the entity plus its neighbors, strongest relationships first. Ids come from the returned nodes and churn on rebuild.
  • search: Finds entities by name (exact, then prefix, then substring); when no name matches, returns the entities nearest in meaning.
  • context_metadata_filter: Keeps only the entities mentioned by contexts matching this metadata filter (context-record metadata only).
  • include_topics: Whether to attach the topic table (topics). Default True; pass False when the caller already holds it.
Returns:

An ExploreKnowledgeBaseGraphResponse dict: nodes, edges (the heaviest, capped; edgesTruncated when cut), totalCount when counted, searchMatch for a search, scopeTruncated when the filter matched too many contexts, and, once the concept layer has been built, topics.

KnowledgeBaseGraphQueryOp = typing.Literal['overview', 'resolve', 'describe', 'subtree', 'docsUnder', 'conceptsOf', 'neighborhood', 'pathBetween', 'globalSummary', 'relationship']
class KnowledgeBaseSearchOptions(typing.TypedDict):
758class KnowledgeBaseSearchOptions(TypedDict, total=False):
759    """Options for knowledge base search."""
760
761    prompt: str
762    """The search prompt."""
763    limit: int
764    """Max number of results to return."""
765    chunkLimit: int
766    """How many chunks to search over (default 100)."""
767    rerankProvider: AiRerankProvider
768    """Reranker provider (default 'cohere')."""
769    rerankScoreThreshold: float
770    """Minimum reranker relevance score a chunk must reach to stay once at least 10 chunks are
771    already kept (default 0.2). Score ranges differ per reranker."""
772    chatModel: AiChatModelSelection
773    """Model to use for answering."""
774    searchMode: Literal["vector", "hybrid", "keyword", "graph"]
775    """Retrieval mode: 'hybrid' (BM25+dense fusion where supported; default on non-graph KBs),
776    'vector' (dense only), 'keyword' (pure lexical — every whitespace-separated term must appear
777    as a literal, case-insensitive substring), or 'graph' (GraphRAG — seeds entities via vector
778    search, expands relationships via graph traversal, maps entities back to their chunks, fused
779    with a hybrid search; requires a ``vectorDbType: 'mongoAtlas'`` KB with ``graphRag.enabled``
780    and is the default there)."""
781    graphOptions: AiKnowledgeBaseGraphSearchOptions
782    """Tuning for ``searchMode: 'graph'``. Ignored for other search modes."""
783    graphFilter: AiKnowledgeBaseGraphFilter
784    """Scopes the search to the documents under one concept of the KB's graph. Requires
785    ``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):
893class MmCategory(TypedDict):
894    """Matchmaking category."""
895
896    id: str
897    description: str

Matchmaking category.

id: str
description: str
class MmEntity(typing.TypedDict):
900class MmEntity(TypedDict, total=False):
901    """Matchmaking entity."""
902
903    id: str
904    content: str
905    categoryId: str
906    metadata: dict[str, Any]

Matchmaking entity.

id: str
content: str
categoryId: str
metadata: dict[str, typing.Any]
class MmFindMatchesOptions(typing.TypedDict):
909class MmFindMatchesOptions(TypedDict, total=False):
910    """Options for finding matches."""
911
912    metadataFilter: dict[str, Any]
913    """AiContextMetadataFilter conditions."""
914    limit: int
915    """Max matches to return (default 100, max 100)."""
916    matchToCategoryId: str
917    """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):
920class MmListEntitiesOptions(TypedDict, total=False):
921    """Options for listing entities."""
922
923    metadataFilter: dict[str, Any]
924    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):
876class StableDiffusionOptions(TypedDict, total=False):
877    """Options for Stable Diffusion Core image generation."""
878
879    modelName: Literal["stable-diffusion-core"]
880    aspectRatio: Literal["16:9", "1:1", "21:9", "2:3", "3:2", "4:5", "5:4", "9:16", "9:21"]
881    negativePrompt: str
882    seed: int
883    stylePreset: str
884    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):
656class TextContextRequest(TypedDict, total=False):
657    """Request to upsert a text context."""
658
659    contextId: str
660    type: Literal["text"]
661    title: str
662    text: str
663    format: Literal["auto", "markdown", "html", "plain"]
664    """Hint about the format of the text. Defaults to 'auto' (sniffed from content)."""
665    extractImages: bool
666    """Whether to extract images embedded in markdown/HTML text. Defaults to False.
667
668    This is the opposite of the file context default, which is True: a pasted blob's inline
669    images are usually layout chrome, while an uploaded document's images are part of its
670    content.
671    """
672    imageMinSizePixels: int
673    imageExtractionModel: AiChatModelSelection
674    metadata: dict[str, Any]
675    options: AiContextTextOptions
676    scopes: list[str]
677    """Access scopes granting read access to this context. Only meaningful on a knowledge base
678    with ``accessControl.mode: 'enforced'``. Omitted on a re-ingest keeps existing scopes; an
679    empty list makes the context visible to every caller.
680    """

Request to upsert a text context.

contextId: str
type: Literal['text']
title: str
text: str
format: Literal['auto', 'markdown', 'html', 'plain']

Hint about the format of the text. Defaults to 'auto' (sniffed from content).

extractImages: bool

Whether to extract images embedded in markdown/HTML text. Defaults to False.

This is the opposite of the file context default, which is True: a pasted blob's inline images are usually layout chrome, while an uploaded document's images are part of its content.

imageMinSizePixels: int
imageExtractionModel: str | IntegrationModelSpec
metadata: dict[str, typing.Any]
scopes: list[str]

Access scopes granting read access to this context. Only meaningful on a knowledge base with accessControl.mode: 'enforced'. Omitted on a re-ingest keeps existing scopes; an empty list makes the context visible to every caller.

class UpsertAgentOptions(typing.TypedDict):
455class UpsertAgentOptions(TypedDict, total=False):
456    """Options for creating/updating an agent."""
457
458    description: str
459    """Description of the agent's purpose."""
460    isPublic: bool
461    """Whether the agent is publicly accessible."""
462    auditLog: bool
463    """Enable audit logging."""
464    auditLogFullContext: bool
465    """Record the full agent context (system instructions and retrieved knowledge-base content) in the audit log."""
466    apiKey: str
467    """API key for this agent."""
468    mcpServer: AiAgentMcpServerConfig
469    """Optional configuration for exposing the agent as an MCP server."""
470    options: AiChatOptions
471    """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):
 994class WebAiSearchResponse(TypedDict):
 995    """Response from AI web search."""
 996
 997    markdownText: str
 998    citedUrls: list[dict[str, str]]
 999    """Pages the answer cites. A terse answer cites nothing even when the search ran."""
1000
1001    consultedUrls: list[str]
1002    """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):
986class WebShortUrlBulkResponse(TypedDict):
987    """Response from creating bulk short URLs."""
988
989    ids: list[str]
990    shortUrls: list[str]
991    expiry: str

Response from creating bulk short URLs.

ids: list[str]
shortUrls: list[str]
expiry: str
class WebShortUrlResponse(typing.TypedDict):
978class WebShortUrlResponse(TypedDict):
979    """Response from creating a short URL."""
980
981    id: str
982    shortUrl: str
983    expiry: str

Response from creating a short URL.

id: str
shortUrl: str
expiry: str