From 30bd94ee44587b8bf1e2e8d76717bfa6fe3c5efd Mon Sep 17 00:00:00 2001 From: CodeMash Date: Fri, 2 Oct 2026 15:48:32 +0300 Subject: [PATCH] feat(ai): [5] end-user chat, project AI settings and assistants, embeddings, AI usage, admin-portal switch 29 methods (sync + async), one per gateway route, written in the repo's module pattern (this SDK is untyped by design; its module generator is gone): - `api.ai` (new): get_end_user_chat_availability, list_end_user_chat_sessions, create_end_user_chat_session, get_end_user_chat_session, rename_end_user_chat_session, delete_end_user_chat_session, pin_end_user_chat_session, archive_end_user_chat_session, get_end_user_chat_entries, set_end_user_chat_entry_feedback, list_end_user_chat_attachments, upload_end_user_chat_attachment, delete_end_user_chat_attachment, list_end_user_chat_memory, forget_end_user_chat_memory, start_end_user_chat_turn. - `hub.account`: get_project_ai_settings, update_project_ai_settings, create_project_ai_assistant, update_project_ai_assistant, delete_project_ai_assistant, get_project_ai_usage, set_admin_portal_enabled. - `hub.ai`: get_embedding_integrations, save_embedding_integration, get_embedding_integration, delete_embedding_integration, test_embedding_integration, set_llm_integration_as_default. Each method has a request-shape test against the capture transport (verb, resolved path, auth and project headers); docs pages and README section. Tests: 657 passed (main 598). ruff and mypy clean. Co-Authored-By: Claude Fable 5.1 --- README.md | 26 ++ docs/api/_index.md | 1 + docs/api/ai.md | 24 ++ docs/hub/account.md | 7 + docs/hub/ai.md | 6 + src/norbix_python/api/__init__.py | 3 + src/norbix_python/api/ai.py | 441 ++++++++++++++++++++++++++++++ src/norbix_python/hub/account.py | 182 ++++++++++++ src/norbix_python/hub/ai.py | 156 +++++++++++ tests/api/test_ai.py | 183 +++++++++++++ tests/hub/test_account.py | 82 ++++++ tests/hub/test_ai.py | 71 +++++ 12 files changed, 1182 insertions(+) create mode 100644 docs/api/ai.md create mode 100644 src/norbix_python/api/ai.py create mode 100644 tests/api/test_ai.py diff --git a/README.md b/README.md index d7c9ac7..6426f95 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,32 @@ A signed-in member can pass `projectId` + `notificationId` instead of `hash`. The gateway answers `401` for a bad or expired link, `403` for a missing permission and `400` for a malformed request. +## End-user AI chat and project AI settings + +A signed-in project user (bearer token from login) talks to the project's AI +assistant through `norbix.api.ai`. `start_end_user_chat_turn` answers at once +with a `turnId`; the answer streams over the gateway's SSE endpoint on the +user's own channel `ai-chat:{projectId}:{authId}` (events `ai.chat.turn.*`, +`ai.chat.session.*`). A subscription to another user's channel is refused with +403 and `responseStatus.errorCode = "AiChatChannelRefused"` โ€” do not retry it. + +```python +avail = norbix.api.ai.get_end_user_chat_availability() +session = norbix.api.ai.create_end_user_chat_session(title="Help") +turn = norbix.api.ai.start_end_user_chat_turn(sessionId=session["id"], message="What can you do?") +entries = norbix.api.ai.get_end_user_chat_entries(session_id=session["id"]) +``` + +Project owners configure the assistant on the Hub: `norbix.hub.account` +(`get_project_ai_settings`, `update_project_ai_settings`, +`create_project_ai_assistant`, `update_project_ai_assistant`, +`delete_project_ai_assistant`, `get_project_ai_usage`, `set_admin_portal_enabled`) +and `norbix.hub.ai` (`get_embedding_integrations`, `save_embedding_integration`, +`get_embedding_integration`, `delete_embedding_integration`, +`test_embedding_integration`, `set_llm_integration_as_default`). Full tables: +[docs/api/ai.md](./docs/api/ai.md), [docs/hub/ai.md](./docs/hub/ai.md), +[docs/hub/account.md](./docs/hub/account.md). + ## Errors ```python diff --git a/docs/api/_index.md b/docs/api/_index.md index 42e199d..5e0e9ac 100644 --- a/docs/api/_index.md +++ b/docs/api/_index.md @@ -2,6 +2,7 @@ | Module | Endpoints | | --- | ---: | +| [`ai`](./ai.md) | 16 | | [`database`](./database.md) | 18 | | [`echo`](./echo.md) | 1 | | [`files`](./files.md) | 10 | diff --git a/docs/api/ai.md b/docs/api/ai.md new file mode 100644 index 0000000..029f018 --- /dev/null +++ b/docs/api/ai.md @@ -0,0 +1,24 @@ +# API ยท Ai + +Access with `norbix.api.ai`. End-user AI chat for a signed-in project user (bearer token from login). + +`start_end_user_chat_turn` answers at once with a `turnId`; tokens, tool calls and the final answer arrive over the gateway's SSE stream on the user's channel `ai-chat:{projectId}:{authId}` (`ai.chat.turn.started|token|tool|completed|failed`, `ai.chat.session.*`). A channel that is not the caller's is refused with 403 and `responseStatus.errorCode = "AiChatChannelRefused"` before the stream starts โ€” do not retry it. + +| Method | Verb | Path | Scope | +| --- | --- | --- | --- | +| `get_end_user_chat_availability` | `GET` | `/{version}/ai/chat/availability` | `project` | +| `list_end_user_chat_sessions` | `GET` | `/{version}/ai/chat/sessions` | `project` | +| `create_end_user_chat_session` | `POST` | `/{version}/ai/chat/sessions` | `project` | +| `get_end_user_chat_session` | `GET` | `/{version}/ai/chat/sessions/{SessionId}` | `project` | +| `rename_end_user_chat_session` | `PATCH` | `/{version}/ai/chat/sessions/{SessionId}` | `project` | +| `delete_end_user_chat_session` | `DELETE` | `/{version}/ai/chat/sessions/{SessionId}` | `project` | +| `pin_end_user_chat_session` | `PUT` | `/{version}/ai/chat/sessions/{SessionId}/pin` | `project` | +| `archive_end_user_chat_session` | `PUT` | `/{version}/ai/chat/sessions/{SessionId}/archive` | `project` | +| `get_end_user_chat_entries` | `GET` | `/{version}/ai/chat/sessions/{SessionId}/entries` | `project` | +| `set_end_user_chat_entry_feedback` | `PUT` | `/{version}/ai/chat/sessions/{SessionId}/entries/{EntryId}/feedback` | `project` | +| `list_end_user_chat_attachments` | `GET` | `/{version}/ai/chat/sessions/{SessionId}/attachments` | `project` | +| `upload_end_user_chat_attachment` | `POST` | `/{version}/ai/chat/sessions/{SessionId}/attachments` | `project` | +| `delete_end_user_chat_attachment` | `DELETE` | `/{version}/ai/chat/attachments/{AttachmentId}` | `project` | +| `list_end_user_chat_memory` | `GET` | `/{version}/ai/chat/memory` | `project` | +| `forget_end_user_chat_memory` | `DELETE` | `/{version}/ai/chat/memory/{NoteId}` | `project` | +| `start_end_user_chat_turn` | `POST` | `/{version}/ai/chat/turn` | `project` | diff --git a/docs/hub/account.md b/docs/hub/account.md index 3cdc98b..89630e0 100644 --- a/docs/hub/account.md +++ b/docs/hub/account.md @@ -40,3 +40,10 @@ Access with `norbix.hub.account`. | `get_account_collaborators` | `GET` | `/{version}/account/collaborators` | `account` | | `send_invite_to_team_member` | `POST` | `/{version}/account/team/member/invite` | `account` | | `get_licenses` | `GET` | `/{version}/account/licenses` | `account` | +| `get_project_ai_settings` | `GET` | `/{version}/account/projects/{projectId}/ai/settings` | `project` | +| `update_project_ai_settings` | `PUT` | `/{version}/account/projects/{projectId}/ai/settings` | `project` | +| `create_project_ai_assistant` | `POST` | `/{version}/account/projects/{projectId}/ai/assistants` | `project` | +| `update_project_ai_assistant` | `PUT` | `/{version}/account/projects/{projectId}/ai/assistants/{assistantId}` | `project` | +| `delete_project_ai_assistant` | `DELETE` | `/{version}/account/projects/{projectId}/ai/assistants/{assistantId}` | `project` | +| `get_project_ai_usage` | `GET` | `/{version}/account/projects/{projectId}/ai/usage` | `project` | +| `set_admin_portal_enabled` | `PUT` | `/{version}/account/projects/{projectId}/admin-portal/enabled` | `project` | diff --git a/docs/hub/ai.md b/docs/hub/ai.md index df21ebe..255e11b 100644 --- a/docs/hub/ai.md +++ b/docs/hub/ai.md @@ -18,3 +18,9 @@ Access with `norbix.hub.ai`. | `get_mcp_integrations` | `GET` | `/{version}/ai/integrations/mcp/integrations` | `project` | | `save_mcp_integration` | `POST` | `/{version}/ai/integrations/mcp/` | `project` | | `test_mcp_integration` | `POST` | `/{version}/ai/integrations/mcp/test` | `project` | +| `get_embedding_integrations` | `GET` | `/{version}/ai/integrations/embeddings` | `project` | +| `save_embedding_integration` | `POST` | `/{version}/ai/integrations/embeddings` | `project` | +| `get_embedding_integration` | `GET` | `/{version}/ai/integrations/embeddings/{Id}` | `project` | +| `delete_embedding_integration` | `DELETE` | `/{version}/ai/integrations/embeddings/{Id}` | `project` | +| `test_embedding_integration` | `POST` | `/{version}/ai/integrations/embeddings/{Id}/test` | `project` | +| `set_llm_integration_as_default` | `PUT` | `/{version}/ai/integrations/llms/{Id}/default` | `project` | diff --git a/src/norbix_python/api/__init__.py b/src/norbix_python/api/__init__.py index 901ff66..d5a3056 100644 --- a/src/norbix_python/api/__init__.py +++ b/src/norbix_python/api/__init__.py @@ -1,6 +1,7 @@ from __future__ import annotations from ..transport import AsyncTransport, Transport +from .ai import AiModule, AsyncAiModule from .database import AsyncDatabaseModule, DatabaseModule from .echo import AsyncEchoModule, EchoModule from .files import AsyncFilesModule, FilesModule @@ -9,6 +10,7 @@ class ApiNamespace: def __init__(self, transport: Transport) -> None: + self.ai = AiModule(transport) self.database = DatabaseModule(transport) self.echo = EchoModule(transport) self.files = FilesModule(transport) @@ -17,6 +19,7 @@ def __init__(self, transport: Transport) -> None: class AsyncApiNamespace: def __init__(self, transport: AsyncTransport) -> None: + self.ai = AsyncAiModule(transport) self.database = AsyncDatabaseModule(transport) self.echo = AsyncEchoModule(transport) self.files = AsyncFilesModule(transport) diff --git a/src/norbix_python/api/ai.py b/src/norbix_python/api/ai.py new file mode 100644 index 0000000..82990b2 --- /dev/null +++ b/src/norbix_python/api/ai.py @@ -0,0 +1,441 @@ +from __future__ import annotations + +from typing import Any + +from ..transport import AsyncTransport, Transport + + +class AiModule: + """End-user AI chat for a signed-in project user. + + ``start_end_user_chat_turn`` answers at once with a ``turnId``; the answer + streams over the gateway's SSE endpoint on the user's own channel + ``ai-chat:{projectId}:{authId}`` (events ``ai.chat.turn.*`` and + ``ai.chat.session.*``). A subscription to another user's channel is + refused with HTTP 403 and ``responseStatus.errorCode = "AiChatChannelRefused"`` + before the stream starts โ€” do not retry it. + """ + + def __init__(self, transport: Transport) -> None: + self._transport = transport + + def get_end_user_chat_availability(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/availability""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/availability", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def list_end_user_chat_sessions(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def create_end_user_chat_session(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/chat/sessions""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions", + method="POST", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def get_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions/{SessionId}""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}", + method="GET", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def rename_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PATCH /{version}/ai/chat/sessions/{SessionId}""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}", + method="PATCH", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def delete_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/chat/sessions/{SessionId}""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}", + method="DELETE", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def pin_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/chat/sessions/{SessionId}/pin""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/pin", + method="PUT", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def archive_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/chat/sessions/{SessionId}/archive""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/archive", + method="PUT", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def get_end_user_chat_entries(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions/{SessionId}/entries""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/entries", + method="GET", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def set_end_user_chat_entry_feedback(self, session_id: str, entry_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/chat/sessions/{SessionId}/entries/{EntryId}/feedback""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/entries/{EntryId}/feedback", + method="PUT", + path_params={"SessionId": session_id, "EntryId": entry_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def list_end_user_chat_attachments(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions/{SessionId}/attachments""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/attachments", + method="GET", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def upload_end_user_chat_attachment(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/chat/sessions/{SessionId}/attachments""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/attachments", + method="POST", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def delete_end_user_chat_attachment(self, attachment_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/chat/attachments/{AttachmentId}""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/attachments/{AttachmentId}", + method="DELETE", + path_params={"AttachmentId": attachment_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def list_end_user_chat_memory(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/memory""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/memory", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def forget_end_user_chat_memory(self, note_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/chat/memory/{NoteId}""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/memory/{NoteId}", + method="DELETE", + path_params={"NoteId": note_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def start_end_user_chat_turn(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/chat/turn""" + return self._transport.send( + target="api", + path="/{version}/ai/chat/turn", + method="POST", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + +class AsyncAiModule: + def __init__(self, transport: AsyncTransport) -> None: + self._transport = transport + + async def get_end_user_chat_availability(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/availability""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/availability", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def list_end_user_chat_sessions(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def create_end_user_chat_session(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/chat/sessions""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions", + method="POST", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def get_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions/{SessionId}""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}", + method="GET", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def rename_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PATCH /{version}/ai/chat/sessions/{SessionId}""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}", + method="PATCH", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def delete_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/chat/sessions/{SessionId}""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}", + method="DELETE", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def pin_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/chat/sessions/{SessionId}/pin""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/pin", + method="PUT", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def archive_end_user_chat_session(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/chat/sessions/{SessionId}/archive""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/archive", + method="PUT", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def get_end_user_chat_entries(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions/{SessionId}/entries""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/entries", + method="GET", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def set_end_user_chat_entry_feedback(self, session_id: str, entry_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/chat/sessions/{SessionId}/entries/{EntryId}/feedback""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/entries/{EntryId}/feedback", + method="PUT", + path_params={"SessionId": session_id, "EntryId": entry_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def list_end_user_chat_attachments(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/sessions/{SessionId}/attachments""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/attachments", + method="GET", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def upload_end_user_chat_attachment(self, session_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/chat/sessions/{SessionId}/attachments""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/sessions/{SessionId}/attachments", + method="POST", + path_params={"SessionId": session_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def delete_end_user_chat_attachment(self, attachment_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/chat/attachments/{AttachmentId}""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/attachments/{AttachmentId}", + method="DELETE", + path_params={"AttachmentId": attachment_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def list_end_user_chat_memory(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/chat/memory""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/memory", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def forget_end_user_chat_memory(self, note_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/chat/memory/{NoteId}""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/memory/{NoteId}", + method="DELETE", + path_params={"NoteId": note_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def start_end_user_chat_turn(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/chat/turn""" + return await self._transport.send( + target="api", + path="/{version}/ai/chat/turn", + method="POST", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) diff --git a/src/norbix_python/hub/account.py b/src/norbix_python/hub/account.py index 594705f..efde193 100644 --- a/src/norbix_python/hub/account.py +++ b/src/norbix_python/hub/account.py @@ -485,6 +485,97 @@ def get_licenses(self, *, timeout: float | None = None, bearer_token: str | None bearer_token=bearer_token, ) + def get_project_ai_settings(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/account/projects/{projectId}/ai/settings""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/settings", + method="GET", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def update_project_ai_settings(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/account/projects/{projectId}/ai/settings""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/settings", + method="PUT", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def create_project_ai_assistant(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/account/projects/{projectId}/ai/assistants""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/assistants", + method="POST", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def update_project_ai_assistant(self, project_id: str, assistant_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/account/projects/{projectId}/ai/assistants/{assistantId}""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/assistants/{assistantId}", + method="PUT", + path_params={"projectId": project_id, "assistantId": assistant_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def delete_project_ai_assistant(self, project_id: str, assistant_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/account/projects/{projectId}/ai/assistants/{assistantId}""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/assistants/{assistantId}", + method="DELETE", + path_params={"projectId": project_id, "assistantId": assistant_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def get_project_ai_usage(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/account/projects/{projectId}/ai/usage""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/usage", + method="GET", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def set_admin_portal_enabled(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/account/projects/{projectId}/admin-portal/enabled""" + return self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/admin-portal/enabled", + method="PUT", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + class AsyncAccountModule: def __init__(self, transport: AsyncTransport) -> None: @@ -965,3 +1056,94 @@ async def get_licenses(self, *, timeout: float | None = None, bearer_token: str timeout=timeout, bearer_token=bearer_token, ) + + async def get_project_ai_settings(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/account/projects/{projectId}/ai/settings""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/settings", + method="GET", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def update_project_ai_settings(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/account/projects/{projectId}/ai/settings""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/settings", + method="PUT", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def create_project_ai_assistant(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/account/projects/{projectId}/ai/assistants""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/assistants", + method="POST", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def update_project_ai_assistant(self, project_id: str, assistant_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/account/projects/{projectId}/ai/assistants/{assistantId}""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/assistants/{assistantId}", + method="PUT", + path_params={"projectId": project_id, "assistantId": assistant_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def delete_project_ai_assistant(self, project_id: str, assistant_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/account/projects/{projectId}/ai/assistants/{assistantId}""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/assistants/{assistantId}", + method="DELETE", + path_params={"projectId": project_id, "assistantId": assistant_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def get_project_ai_usage(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/account/projects/{projectId}/ai/usage""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/ai/usage", + method="GET", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def set_admin_portal_enabled(self, project_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/account/projects/{projectId}/admin-portal/enabled""" + return await self._transport.send( + target="hub", + path="/{version}/account/projects/{projectId}/admin-portal/enabled", + method="PUT", + path_params={"projectId": project_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) diff --git a/src/norbix_python/hub/ai.py b/src/norbix_python/hub/ai.py index d83f914..d4b925a 100644 --- a/src/norbix_python/hub/ai.py +++ b/src/norbix_python/hub/ai.py @@ -191,6 +191,84 @@ def test_mcp_integration(self, *, timeout: float | None = None, bearer_token: st bearer_token=bearer_token, ) + def get_embedding_integrations(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/integrations/embeddings""" + return self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def save_embedding_integration(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/integrations/embeddings""" + return self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings", + method="POST", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def get_embedding_integration(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/integrations/embeddings/{Id}""" + return self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings/{Id}", + method="GET", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def delete_embedding_integration(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/integrations/embeddings/{Id}""" + return self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings/{Id}", + method="DELETE", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def test_embedding_integration(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/integrations/embeddings/{Id}/test""" + return self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings/{Id}/test", + method="POST", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + def set_llm_integration_as_default(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/integrations/llms/{Id}/default""" + return self._transport.send( + target="hub", + path="/{version}/ai/integrations/llms/{Id}/default", + method="PUT", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + class AsyncAiModule: def __init__(self, transport: AsyncTransport) -> None: @@ -377,3 +455,81 @@ async def test_mcp_integration(self, *, timeout: float | None = None, bearer_tok timeout=timeout, bearer_token=bearer_token, ) + + async def get_embedding_integrations(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/integrations/embeddings""" + return await self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings", + method="GET", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def save_embedding_integration(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/integrations/embeddings""" + return await self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings", + method="POST", + path_params={}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def get_embedding_integration(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/ai/integrations/embeddings/{Id}""" + return await self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings/{Id}", + method="GET", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def delete_embedding_integration(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """DELETE /{version}/ai/integrations/embeddings/{Id}""" + return await self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings/{Id}", + method="DELETE", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def test_embedding_integration(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """POST /{version}/ai/integrations/embeddings/{Id}/test""" + return await self._transport.send( + target="hub", + path="/{version}/ai/integrations/embeddings/{Id}/test", + method="POST", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + + async def set_llm_integration_as_default(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """PUT /{version}/ai/integrations/llms/{Id}/default""" + return await self._transport.send( + target="hub", + path="/{version}/ai/integrations/llms/{Id}/default", + method="PUT", + path_params={"Id": id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) diff --git a/tests/api/test_ai.py b/tests/api/test_ai.py new file mode 100644 index 0000000..2d9c1dc --- /dev/null +++ b/tests/api/test_ai.py @@ -0,0 +1,183 @@ +from __future__ import annotations + +from ..helpers import make_client +def test_api_ai_wave3_module_surface() -> None: + client, _ = make_client() + module = client.api.ai + assert callable(module.get_end_user_chat_availability) + assert callable(module.list_end_user_chat_sessions) + assert callable(module.create_end_user_chat_session) + assert callable(module.get_end_user_chat_session) + assert callable(module.rename_end_user_chat_session) + assert callable(module.delete_end_user_chat_session) + assert callable(module.pin_end_user_chat_session) + assert callable(module.archive_end_user_chat_session) + assert callable(module.get_end_user_chat_entries) + assert callable(module.set_end_user_chat_entry_feedback) + assert callable(module.list_end_user_chat_attachments) + assert callable(module.upload_end_user_chat_attachment) + assert callable(module.delete_end_user_chat_attachment) + assert callable(module.list_end_user_chat_memory) + assert callable(module.forget_end_user_chat_memory) + assert callable(module.start_end_user_chat_turn) + + +def test_api_ai_get_end_user_chat_availability_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.get_end_user_chat_availability() + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/chat/availability') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_list_end_user_chat_sessions_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.list_end_user_chat_sessions() + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_create_end_user_chat_session_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.create_end_user_chat_session() + assert transport.last_request is not None + assert transport.last_request['method'] == 'POST' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_get_end_user_chat_session_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.get_end_user_chat_session(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_rename_end_user_chat_session_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.rename_end_user_chat_session(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PATCH' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_delete_end_user_chat_session_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.delete_end_user_chat_session(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'DELETE' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_pin_end_user_chat_session_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.pin_end_user_chat_session(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId/pin') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_archive_end_user_chat_session_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.archive_end_user_chat_session(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId/archive') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_get_end_user_chat_entries_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.get_end_user_chat_entries(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId/entries') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_set_end_user_chat_entry_feedback_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.set_end_user_chat_entry_feedback(session_id="stub-SessionId", entry_id="stub-EntryId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId/entries/stub-EntryId/feedback') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_list_end_user_chat_attachments_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.list_end_user_chat_attachments(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId/attachments') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_upload_end_user_chat_attachment_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.upload_end_user_chat_attachment(session_id="stub-SessionId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'POST' + assert transport.last_request['url'].endswith('/v2/ai/chat/sessions/stub-SessionId/attachments') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_delete_end_user_chat_attachment_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.delete_end_user_chat_attachment(attachment_id="stub-AttachmentId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'DELETE' + assert transport.last_request['url'].endswith('/v2/ai/chat/attachments/stub-AttachmentId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_list_end_user_chat_memory_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.list_end_user_chat_memory() + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/chat/memory') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_forget_end_user_chat_memory_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.forget_end_user_chat_memory(note_id="stub-NoteId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'DELETE' + assert transport.last_request['url'].endswith('/v2/ai/chat/memory/stub-NoteId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_api_ai_start_end_user_chat_turn_request_shape() -> None: + client, transport = make_client(account_id=None) + client.api.ai.start_end_user_chat_turn() + assert transport.last_request is not None + assert transport.last_request['method'] == 'POST' + assert transport.last_request['url'].endswith('/v2/ai/chat/turn') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + diff --git a/tests/hub/test_account.py b/tests/hub/test_account.py index fab2215..e9e021e 100644 --- a/tests/hub/test_account.py +++ b/tests/hub/test_account.py @@ -620,3 +620,85 @@ def test_hub_account_get_licenses_requires_account_scope() -> None: assert exc.code == 'NORBIX_ACCOUNT_SCOPE_REQUIRED' else: raise AssertionError('Expected account scope error') + +def test_hub_account_wave3_module_surface() -> None: + client, _ = make_client() + module = client.hub.account + assert callable(module.get_project_ai_settings) + assert callable(module.update_project_ai_settings) + assert callable(module.create_project_ai_assistant) + assert callable(module.update_project_ai_assistant) + assert callable(module.delete_project_ai_assistant) + assert callable(module.get_project_ai_usage) + assert callable(module.set_admin_portal_enabled) + + +def test_hub_account_get_project_ai_settings_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.get_project_ai_settings(project_id="stub-projectId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/ai/settings') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_account_update_project_ai_settings_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.update_project_ai_settings(project_id="stub-projectId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/ai/settings') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_account_create_project_ai_assistant_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.create_project_ai_assistant(project_id="stub-projectId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'POST' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/ai/assistants') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_account_update_project_ai_assistant_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.update_project_ai_assistant(project_id="stub-projectId", assistant_id="stub-assistantId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/ai/assistants/stub-assistantId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_account_delete_project_ai_assistant_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.delete_project_ai_assistant(project_id="stub-projectId", assistant_id="stub-assistantId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'DELETE' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/ai/assistants/stub-assistantId') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_account_get_project_ai_usage_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.get_project_ai_usage(project_id="stub-projectId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/ai/usage') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_account_set_admin_portal_enabled_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.account.set_admin_portal_enabled(project_id="stub-projectId") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/account/projects/stub-projectId/admin-portal/enabled') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + diff --git a/tests/hub/test_ai.py b/tests/hub/test_ai.py index d7b9def..4f8cd90 100644 --- a/tests/hub/test_ai.py +++ b/tests/hub/test_ai.py @@ -120,3 +120,74 @@ def test_hub_ai_test_mcp_integration_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request is not None assert transport.last_request['url'].startswith('https://') + +def test_hub_ai_wave3_module_surface() -> None: + client, _ = make_client() + module = client.hub.ai + assert callable(module.get_embedding_integrations) + assert callable(module.save_embedding_integration) + assert callable(module.get_embedding_integration) + assert callable(module.delete_embedding_integration) + assert callable(module.test_embedding_integration) + assert callable(module.set_llm_integration_as_default) + + +def test_hub_ai_get_embedding_integrations_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.ai.get_embedding_integrations() + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/integrations/embeddings') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_ai_save_embedding_integration_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.ai.save_embedding_integration() + assert transport.last_request is not None + assert transport.last_request['method'] == 'POST' + assert transport.last_request['url'].endswith('/v2/ai/integrations/embeddings') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_ai_get_embedding_integration_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.ai.get_embedding_integration(id="stub-Id") + assert transport.last_request is not None + assert transport.last_request['method'] == 'GET' + assert transport.last_request['url'].endswith('/v2/ai/integrations/embeddings/stub-Id') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_ai_delete_embedding_integration_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.ai.delete_embedding_integration(id="stub-Id") + assert transport.last_request is not None + assert transport.last_request['method'] == 'DELETE' + assert transport.last_request['url'].endswith('/v2/ai/integrations/embeddings/stub-Id') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_ai_test_embedding_integration_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.ai.test_embedding_integration(id="stub-Id") + assert transport.last_request is not None + assert transport.last_request['method'] == 'POST' + assert transport.last_request['url'].endswith('/v2/ai/integrations/embeddings/stub-Id/test') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + + +def test_hub_ai_set_llm_integration_as_default_request_shape() -> None: + client, transport = make_client(account_id=None) + client.hub.ai.set_llm_integration_as_default(id="stub-Id") + assert transport.last_request is not None + assert transport.last_request['method'] == 'PUT' + assert transport.last_request['url'].endswith('/v2/ai/integrations/llms/stub-Id/default') + assert transport.last_request['headers']['authorization'] == 'Bearer test-token' + assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' +