diff --git a/.github/workflows/stlc-sync.yml b/.github/workflows/stlc-sync.yml
index e45a6fb1..ee3c2f48 100644
--- a/.github/workflows/stlc-sync.yml
+++ b/.github/workflows/stlc-sync.yml
@@ -76,6 +76,8 @@ jobs:
- name: Sync production to staging (fast-forward)
if: steps.diff.outputs.behind == 'true'
+ env:
+ PUSH_TOKEN: ${{ secrets.PRODUCTION_REPO_TOKEN }}
run: |
# Refuse unless staging is an ancestor of production: otherwise the
# trunks have forked and a fast-forward would be unsafe.
@@ -83,7 +85,17 @@ jobs:
echo "::error title=Back-sync blocked::staging main is not an ancestor of production/main."
exit 1
fi
- git push origin production/main:refs/heads/main
+ # Push as the automation identity (PRODUCTION_REPO_TOKEN), not GITHUB_TOKEN:
+ # staging main requires a pull request for humans and only the automation's
+ # repository role bypasses that rule. A token push (unlike GITHUB_TOKEN) also
+ # triggers seal-dispatch, so the config repo re-seals right away instead of
+ # at the next scheduled sync. Blank the persisted GITHUB_TOKEN header first.
+ if [ -n "${PUSH_TOKEN:-}" ]; then
+ git -c "http.https://github.com/.extraheader=" push \
+ "https://x-access-token:${PUSH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" production/main:refs/heads/main
+ else
+ git push origin production/main:refs/heads/main
+ fi
echo "Fast-forwarded staging/main to production/main."
- name: Alert on failure
diff --git a/.stats.yml b/.stats.yml
index e8ba8c9f..b4620cc9 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1 +1 @@
-configured_endpoints: 32
+configured_endpoints: 58
diff --git a/api.md b/api.md
index 08062d22..9048346f 100644
--- a/api.md
+++ b/api.md
@@ -186,12 +186,13 @@ Methods:
Types:
```python
-from openlayer.types.storage import PresignedURLCreateResponse
+from openlayer.types.storage import PresignedURLCreateResponse, PresignedURLRetrieveResponse
```
Methods:
- client.storage.presigned_url.create(\*\*params) -> PresignedURLCreateResponse
+- client.storage.presigned_url.retrieve(\*\*params) -> PresignedURLRetrieveResponse
# Tests
@@ -205,3 +206,149 @@ Methods:
- client.tests.evaluate(test_id, \*\*params) -> TestEvaluateResponse
- client.tests.list_results(test_id, \*\*params) -> TestListResultsResponse
+
+# BackgroundTasks
+
+Types:
+
+```python
+from openlayer.types import BackgroundTaskRetrieveResponse
+```
+
+Methods:
+
+- client.background_tasks.retrieve(task_id) -> BackgroundTaskRetrieveResponse
+
+# Governance
+
+## Frameworks
+
+Types:
+
+```python
+from openlayer.types.governance import (
+ FrameworkCreateResponse,
+ FrameworkRetrieveResponse,
+ FrameworkUpdateResponse,
+ FrameworkListResponse,
+ FrameworkExportResponse,
+ FrameworkListProjectRuleStatsResponse,
+ FrameworkListProjectsResponse,
+ FrameworkListRulesResponse,
+)
+```
+
+Methods:
+
+- client.governance.frameworks.create(workspace_id, \*\*params) -> FrameworkCreateResponse
+- client.governance.frameworks.retrieve(framework_id) -> FrameworkRetrieveResponse
+- client.governance.frameworks.update(framework_id, \*\*params) -> FrameworkUpdateResponse
+- client.governance.frameworks.list(workspace_id, \*\*params) -> FrameworkListResponse
+- client.governance.frameworks.export(framework_id, \*\*params) -> FrameworkExportResponse
+- client.governance.frameworks.list_project_rule_stats(framework_id, \*\*params) -> FrameworkListProjectRuleStatsResponse
+- client.governance.frameworks.list_projects(framework_id, \*\*params) -> FrameworkListProjectsResponse
+- client.governance.frameworks.list_rules(framework_id, \*\*params) -> FrameworkListRulesResponse
+
+### Documents
+
+Types:
+
+```python
+from openlayer.types.governance.frameworks import DocumentRetrieveResponse, DocumentListResponse
+```
+
+Methods:
+
+- client.governance.frameworks.documents.retrieve(document_id, \*, framework_id) -> DocumentRetrieveResponse
+- client.governance.frameworks.documents.list(framework_id, \*\*params) -> DocumentListResponse
+
+### Sections
+
+Types:
+
+```python
+from openlayer.types.governance.frameworks import SectionListRulesResponse
+```
+
+Methods:
+
+- client.governance.frameworks.sections.list_rules(section_id, \*, framework_id, \*\*params) -> SectionListRulesResponse
+
+### Subsections
+
+Types:
+
+```python
+from openlayer.types.governance.frameworks import SubsectionListRulesResponse
+```
+
+Methods:
+
+- client.governance.frameworks.subsections.list_rules(subsection_id, \*, framework_id, \*\*params) -> SubsectionListRulesResponse
+
+## Rules
+
+Types:
+
+```python
+from openlayer.types.governance import (
+ RuleCreateResponse,
+ RuleRetrieveResponse,
+ RuleUpdateResponse,
+ RuleListResponse,
+)
+```
+
+Methods:
+
+- client.governance.rules.create(workspace_id, \*\*params) -> RuleCreateResponse
+- client.governance.rules.retrieve(rule_id) -> RuleRetrieveResponse
+- client.governance.rules.update(rule_id, \*\*params) -> RuleUpdateResponse
+- client.governance.rules.list(workspace_id, \*\*params) -> RuleListResponse
+- client.governance.rules.delete(rule_id) -> None
+
+## RuleResults
+
+Types:
+
+```python
+from openlayer.types.governance import (
+ RuleResultRetrieveResponse,
+ RuleResultUpdateResponse,
+ RuleResultListResponse,
+ RuleResultCreateEvidenceResponse,
+ RuleResultListEvidenceResponse,
+)
+```
+
+Methods:
+
+- client.governance.rule_results.retrieve(rule_result_id) -> RuleResultRetrieveResponse
+- client.governance.rule_results.update(rule_result_id, \*\*params) -> RuleResultUpdateResponse
+- client.governance.rule_results.list(workspace_id, \*\*params) -> RuleResultListResponse
+- client.governance.rule_results.create_evidence(rule_result_id, \*\*params) -> RuleResultCreateEvidenceResponse
+- client.governance.rule_results.list_evidence(rule_result_id, \*\*params) -> RuleResultListEvidenceResponse
+
+## RuleStats
+
+Types:
+
+```python
+from openlayer.types.governance import RuleStatRetrieveResponse
+```
+
+Methods:
+
+- client.governance.rule_stats.retrieve(workspace_id, \*\*params) -> RuleStatRetrieveResponse
+
+## RuleTags
+
+Types:
+
+```python
+from openlayer.types.governance import RuleTagListResponse
+```
+
+Methods:
+
+- client.governance.rule_tags.list(workspace_id, \*\*params) -> RuleTagListResponse
diff --git a/scripts/mock b/scripts/mock
index 099a4c9c..45416b90 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -16,7 +16,7 @@ elif [ -n "$STAINLESS_OPENAPI_SPEC_URL" ]; then
URL="$STAINLESS_OPENAPI_SPEC_URL"
else
# Embedded OpenAPI spec (base64-encoded, gzipped)
- EMBEDDED_SPEC="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"
+ EMBEDDED_SPEC="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"
SPEC_TMPFILE="$(mktemp)"
trap 'rm -f "$SPEC_TMPFILE"' EXIT
echo "$EMBEDDED_SPEC" | base64 --decode | gunzip > "$SPEC_TMPFILE"
diff --git a/src/openlayer/_client.py b/src/openlayer/_client.py
index 5d5ad74b..4a226707 100644
--- a/src/openlayer/_client.py
+++ b/src/openlayer/_client.py
@@ -36,11 +36,22 @@
)
if TYPE_CHECKING:
- from .resources import tests, commits, storage, projects, workspaces, inference_pipelines
+ from .resources import (
+ tests,
+ commits,
+ storage,
+ projects,
+ governance,
+ workspaces,
+ background_tasks,
+ inference_pipelines,
+ )
from .resources.tests import TestsResource, AsyncTestsResource
from .resources.commits.commits import CommitsResource, AsyncCommitsResource
from .resources.storage.storage import StorageResource, AsyncStorageResource
+ from .resources.background_tasks import BackgroundTasksResource, AsyncBackgroundTasksResource
from .resources.projects.projects import ProjectsResource, AsyncProjectsResource
+ from .resources.governance.governance import GovernanceResource, AsyncGovernanceResource
from .resources.workspaces.workspaces import WorkspacesResource, AsyncWorkspacesResource
from .resources.inference_pipelines.inference_pipelines import (
InferencePipelinesResource,
@@ -155,6 +166,18 @@ def tests(self) -> TestsResource:
return TestsResource(self)
+ @cached_property
+ def background_tasks(self) -> BackgroundTasksResource:
+ from .resources.background_tasks import BackgroundTasksResource
+
+ return BackgroundTasksResource(self)
+
+ @cached_property
+ def governance(self) -> GovernanceResource:
+ from .resources.governance import GovernanceResource
+
+ return GovernanceResource(self)
+
@cached_property
def with_raw_response(self) -> OpenlayerWithRawResponse:
return OpenlayerWithRawResponse(self)
@@ -375,6 +398,18 @@ def tests(self) -> AsyncTestsResource:
return AsyncTestsResource(self)
+ @cached_property
+ def background_tasks(self) -> AsyncBackgroundTasksResource:
+ from .resources.background_tasks import AsyncBackgroundTasksResource
+
+ return AsyncBackgroundTasksResource(self)
+
+ @cached_property
+ def governance(self) -> AsyncGovernanceResource:
+ from .resources.governance import AsyncGovernanceResource
+
+ return AsyncGovernanceResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncOpenlayerWithRawResponse:
return AsyncOpenlayerWithRawResponse(self)
@@ -541,6 +576,18 @@ def tests(self) -> tests.TestsResourceWithRawResponse:
return TestsResourceWithRawResponse(self._client.tests)
+ @cached_property
+ def background_tasks(self) -> background_tasks.BackgroundTasksResourceWithRawResponse:
+ from .resources.background_tasks import BackgroundTasksResourceWithRawResponse
+
+ return BackgroundTasksResourceWithRawResponse(self._client.background_tasks)
+
+ @cached_property
+ def governance(self) -> governance.GovernanceResourceWithRawResponse:
+ from .resources.governance import GovernanceResourceWithRawResponse
+
+ return GovernanceResourceWithRawResponse(self._client.governance)
+
class AsyncOpenlayerWithRawResponse:
_client: AsyncOpenlayer
@@ -584,6 +631,18 @@ def tests(self) -> tests.AsyncTestsResourceWithRawResponse:
return AsyncTestsResourceWithRawResponse(self._client.tests)
+ @cached_property
+ def background_tasks(self) -> background_tasks.AsyncBackgroundTasksResourceWithRawResponse:
+ from .resources.background_tasks import AsyncBackgroundTasksResourceWithRawResponse
+
+ return AsyncBackgroundTasksResourceWithRawResponse(self._client.background_tasks)
+
+ @cached_property
+ def governance(self) -> governance.AsyncGovernanceResourceWithRawResponse:
+ from .resources.governance import AsyncGovernanceResourceWithRawResponse
+
+ return AsyncGovernanceResourceWithRawResponse(self._client.governance)
+
class OpenlayerWithStreamedResponse:
_client: Openlayer
@@ -627,6 +686,18 @@ def tests(self) -> tests.TestsResourceWithStreamingResponse:
return TestsResourceWithStreamingResponse(self._client.tests)
+ @cached_property
+ def background_tasks(self) -> background_tasks.BackgroundTasksResourceWithStreamingResponse:
+ from .resources.background_tasks import BackgroundTasksResourceWithStreamingResponse
+
+ return BackgroundTasksResourceWithStreamingResponse(self._client.background_tasks)
+
+ @cached_property
+ def governance(self) -> governance.GovernanceResourceWithStreamingResponse:
+ from .resources.governance import GovernanceResourceWithStreamingResponse
+
+ return GovernanceResourceWithStreamingResponse(self._client.governance)
+
class AsyncOpenlayerWithStreamedResponse:
_client: AsyncOpenlayer
@@ -670,6 +741,18 @@ def tests(self) -> tests.AsyncTestsResourceWithStreamingResponse:
return AsyncTestsResourceWithStreamingResponse(self._client.tests)
+ @cached_property
+ def background_tasks(self) -> background_tasks.AsyncBackgroundTasksResourceWithStreamingResponse:
+ from .resources.background_tasks import AsyncBackgroundTasksResourceWithStreamingResponse
+
+ return AsyncBackgroundTasksResourceWithStreamingResponse(self._client.background_tasks)
+
+ @cached_property
+ def governance(self) -> governance.AsyncGovernanceResourceWithStreamingResponse:
+ from .resources.governance import AsyncGovernanceResourceWithStreamingResponse
+
+ return AsyncGovernanceResourceWithStreamingResponse(self._client.governance)
+
Client = Openlayer
diff --git a/src/openlayer/resources/__init__.py b/src/openlayer/resources/__init__.py
index 291546cd..c6772d66 100644
--- a/src/openlayer/resources/__init__.py
+++ b/src/openlayer/resources/__init__.py
@@ -32,6 +32,14 @@
ProjectsResourceWithStreamingResponse,
AsyncProjectsResourceWithStreamingResponse,
)
+from .governance import (
+ GovernanceResource,
+ AsyncGovernanceResource,
+ GovernanceResourceWithRawResponse,
+ AsyncGovernanceResourceWithRawResponse,
+ GovernanceResourceWithStreamingResponse,
+ AsyncGovernanceResourceWithStreamingResponse,
+)
from .workspaces import (
WorkspacesResource,
AsyncWorkspacesResource,
@@ -40,6 +48,14 @@
WorkspacesResourceWithStreamingResponse,
AsyncWorkspacesResourceWithStreamingResponse,
)
+from .background_tasks import (
+ BackgroundTasksResource,
+ AsyncBackgroundTasksResource,
+ BackgroundTasksResourceWithRawResponse,
+ AsyncBackgroundTasksResourceWithRawResponse,
+ BackgroundTasksResourceWithStreamingResponse,
+ AsyncBackgroundTasksResourceWithStreamingResponse,
+)
from .inference_pipelines import (
InferencePipelinesResource,
AsyncInferencePipelinesResource,
@@ -86,4 +102,16 @@
"AsyncTestsResourceWithRawResponse",
"TestsResourceWithStreamingResponse",
"AsyncTestsResourceWithStreamingResponse",
+ "BackgroundTasksResource",
+ "AsyncBackgroundTasksResource",
+ "BackgroundTasksResourceWithRawResponse",
+ "AsyncBackgroundTasksResourceWithRawResponse",
+ "BackgroundTasksResourceWithStreamingResponse",
+ "AsyncBackgroundTasksResourceWithStreamingResponse",
+ "GovernanceResource",
+ "AsyncGovernanceResource",
+ "GovernanceResourceWithRawResponse",
+ "AsyncGovernanceResourceWithRawResponse",
+ "GovernanceResourceWithStreamingResponse",
+ "AsyncGovernanceResourceWithStreamingResponse",
]
diff --git a/src/openlayer/resources/background_tasks.py b/src/openlayer/resources/background_tasks.py
new file mode 100644
index 00000000..b3ae6088
--- /dev/null
+++ b/src/openlayer/resources/background_tasks.py
@@ -0,0 +1,172 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from .._types import Body, Query, Headers, NotGiven, not_given
+from .._utils import path_template
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.background_task_retrieve_response import BackgroundTaskRetrieveResponse
+
+__all__ = ["BackgroundTasksResource", "AsyncBackgroundTasksResource"]
+
+
+class BackgroundTasksResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> BackgroundTasksResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return BackgroundTasksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> BackgroundTasksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return BackgroundTasksResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ task_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BackgroundTaskRetrieveResponse:
+ """
+ Retrieve a background task's status, progress and results.
+
+ Endpoints that cannot answer within one request queue a task and hand back its
+ id -- for example `POST /frameworks/{frameworkId}/export`. Poll this endpoint
+ until `complete` is `true`, then read what the task produced from `outputs`.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not task_id:
+ raise ValueError(f"Expected a non-empty value for `task_id` but received {task_id!r}")
+ return self._get(
+ path_template("/background-tasks/{task_id}", task_id=task_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BackgroundTaskRetrieveResponse,
+ )
+
+
+class AsyncBackgroundTasksResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncBackgroundTasksResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncBackgroundTasksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncBackgroundTasksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncBackgroundTasksResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ task_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BackgroundTaskRetrieveResponse:
+ """
+ Retrieve a background task's status, progress and results.
+
+ Endpoints that cannot answer within one request queue a task and hand back its
+ id -- for example `POST /frameworks/{frameworkId}/export`. Poll this endpoint
+ until `complete` is `true`, then read what the task produced from `outputs`.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not task_id:
+ raise ValueError(f"Expected a non-empty value for `task_id` but received {task_id!r}")
+ return await self._get(
+ path_template("/background-tasks/{task_id}", task_id=task_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BackgroundTaskRetrieveResponse,
+ )
+
+
+class BackgroundTasksResourceWithRawResponse:
+ def __init__(self, background_tasks: BackgroundTasksResource) -> None:
+ self._background_tasks = background_tasks
+
+ self.retrieve = to_raw_response_wrapper(
+ background_tasks.retrieve,
+ )
+
+
+class AsyncBackgroundTasksResourceWithRawResponse:
+ def __init__(self, background_tasks: AsyncBackgroundTasksResource) -> None:
+ self._background_tasks = background_tasks
+
+ self.retrieve = async_to_raw_response_wrapper(
+ background_tasks.retrieve,
+ )
+
+
+class BackgroundTasksResourceWithStreamingResponse:
+ def __init__(self, background_tasks: BackgroundTasksResource) -> None:
+ self._background_tasks = background_tasks
+
+ self.retrieve = to_streamed_response_wrapper(
+ background_tasks.retrieve,
+ )
+
+
+class AsyncBackgroundTasksResourceWithStreamingResponse:
+ def __init__(self, background_tasks: AsyncBackgroundTasksResource) -> None:
+ self._background_tasks = background_tasks
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ background_tasks.retrieve,
+ )
diff --git a/src/openlayer/resources/governance/__init__.py b/src/openlayer/resources/governance/__init__.py
new file mode 100644
index 00000000..cce59fe8
--- /dev/null
+++ b/src/openlayer/resources/governance/__init__.py
@@ -0,0 +1,89 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .rules import (
+ RulesResource,
+ AsyncRulesResource,
+ RulesResourceWithRawResponse,
+ AsyncRulesResourceWithRawResponse,
+ RulesResourceWithStreamingResponse,
+ AsyncRulesResourceWithStreamingResponse,
+)
+from .rule_tags import (
+ RuleTagsResource,
+ AsyncRuleTagsResource,
+ RuleTagsResourceWithRawResponse,
+ AsyncRuleTagsResourceWithRawResponse,
+ RuleTagsResourceWithStreamingResponse,
+ AsyncRuleTagsResourceWithStreamingResponse,
+)
+from .frameworks import (
+ FrameworksResource,
+ AsyncFrameworksResource,
+ FrameworksResourceWithRawResponse,
+ AsyncFrameworksResourceWithRawResponse,
+ FrameworksResourceWithStreamingResponse,
+ AsyncFrameworksResourceWithStreamingResponse,
+)
+from .governance import (
+ GovernanceResource,
+ AsyncGovernanceResource,
+ GovernanceResourceWithRawResponse,
+ AsyncGovernanceResourceWithRawResponse,
+ GovernanceResourceWithStreamingResponse,
+ AsyncGovernanceResourceWithStreamingResponse,
+)
+from .rule_stats import (
+ RuleStatsResource,
+ AsyncRuleStatsResource,
+ RuleStatsResourceWithRawResponse,
+ AsyncRuleStatsResourceWithRawResponse,
+ RuleStatsResourceWithStreamingResponse,
+ AsyncRuleStatsResourceWithStreamingResponse,
+)
+from .rule_results import (
+ RuleResultsResource,
+ AsyncRuleResultsResource,
+ RuleResultsResourceWithRawResponse,
+ AsyncRuleResultsResourceWithRawResponse,
+ RuleResultsResourceWithStreamingResponse,
+ AsyncRuleResultsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "FrameworksResource",
+ "AsyncFrameworksResource",
+ "FrameworksResourceWithRawResponse",
+ "AsyncFrameworksResourceWithRawResponse",
+ "FrameworksResourceWithStreamingResponse",
+ "AsyncFrameworksResourceWithStreamingResponse",
+ "RulesResource",
+ "AsyncRulesResource",
+ "RulesResourceWithRawResponse",
+ "AsyncRulesResourceWithRawResponse",
+ "RulesResourceWithStreamingResponse",
+ "AsyncRulesResourceWithStreamingResponse",
+ "RuleResultsResource",
+ "AsyncRuleResultsResource",
+ "RuleResultsResourceWithRawResponse",
+ "AsyncRuleResultsResourceWithRawResponse",
+ "RuleResultsResourceWithStreamingResponse",
+ "AsyncRuleResultsResourceWithStreamingResponse",
+ "RuleStatsResource",
+ "AsyncRuleStatsResource",
+ "RuleStatsResourceWithRawResponse",
+ "AsyncRuleStatsResourceWithRawResponse",
+ "RuleStatsResourceWithStreamingResponse",
+ "AsyncRuleStatsResourceWithStreamingResponse",
+ "RuleTagsResource",
+ "AsyncRuleTagsResource",
+ "RuleTagsResourceWithRawResponse",
+ "AsyncRuleTagsResourceWithRawResponse",
+ "RuleTagsResourceWithStreamingResponse",
+ "AsyncRuleTagsResourceWithStreamingResponse",
+ "GovernanceResource",
+ "AsyncGovernanceResource",
+ "GovernanceResourceWithRawResponse",
+ "AsyncGovernanceResourceWithRawResponse",
+ "GovernanceResourceWithStreamingResponse",
+ "AsyncGovernanceResourceWithStreamingResponse",
+]
diff --git a/src/openlayer/resources/governance/frameworks/__init__.py b/src/openlayer/resources/governance/frameworks/__init__.py
new file mode 100644
index 00000000..bf062b6f
--- /dev/null
+++ b/src/openlayer/resources/governance/frameworks/__init__.py
@@ -0,0 +1,61 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .sections import (
+ SectionsResource,
+ AsyncSectionsResource,
+ SectionsResourceWithRawResponse,
+ AsyncSectionsResourceWithRawResponse,
+ SectionsResourceWithStreamingResponse,
+ AsyncSectionsResourceWithStreamingResponse,
+)
+from .documents import (
+ DocumentsResource,
+ AsyncDocumentsResource,
+ DocumentsResourceWithRawResponse,
+ AsyncDocumentsResourceWithRawResponse,
+ DocumentsResourceWithStreamingResponse,
+ AsyncDocumentsResourceWithStreamingResponse,
+)
+from .frameworks import (
+ FrameworksResource,
+ AsyncFrameworksResource,
+ FrameworksResourceWithRawResponse,
+ AsyncFrameworksResourceWithRawResponse,
+ FrameworksResourceWithStreamingResponse,
+ AsyncFrameworksResourceWithStreamingResponse,
+)
+from .subsections import (
+ SubsectionsResource,
+ AsyncSubsectionsResource,
+ SubsectionsResourceWithRawResponse,
+ AsyncSubsectionsResourceWithRawResponse,
+ SubsectionsResourceWithStreamingResponse,
+ AsyncSubsectionsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "DocumentsResource",
+ "AsyncDocumentsResource",
+ "DocumentsResourceWithRawResponse",
+ "AsyncDocumentsResourceWithRawResponse",
+ "DocumentsResourceWithStreamingResponse",
+ "AsyncDocumentsResourceWithStreamingResponse",
+ "SectionsResource",
+ "AsyncSectionsResource",
+ "SectionsResourceWithRawResponse",
+ "AsyncSectionsResourceWithRawResponse",
+ "SectionsResourceWithStreamingResponse",
+ "AsyncSectionsResourceWithStreamingResponse",
+ "SubsectionsResource",
+ "AsyncSubsectionsResource",
+ "SubsectionsResourceWithRawResponse",
+ "AsyncSubsectionsResourceWithRawResponse",
+ "SubsectionsResourceWithStreamingResponse",
+ "AsyncSubsectionsResourceWithStreamingResponse",
+ "FrameworksResource",
+ "AsyncFrameworksResource",
+ "FrameworksResourceWithRawResponse",
+ "AsyncFrameworksResourceWithRawResponse",
+ "FrameworksResourceWithStreamingResponse",
+ "AsyncFrameworksResourceWithStreamingResponse",
+]
diff --git a/src/openlayer/resources/governance/frameworks/documents.py b/src/openlayer/resources/governance/frameworks/documents.py
new file mode 100644
index 00000000..b863d104
--- /dev/null
+++ b/src/openlayer/resources/governance/frameworks/documents.py
@@ -0,0 +1,302 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.governance.frameworks import document_list_params
+from ....types.governance.frameworks.document_list_response import DocumentListResponse
+from ....types.governance.frameworks.document_retrieve_response import DocumentRetrieveResponse
+
+__all__ = ["DocumentsResource", "AsyncDocumentsResource"]
+
+
+class DocumentsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> DocumentsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return DocumentsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> DocumentsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return DocumentsResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ document_id: str,
+ *,
+ framework_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DocumentRetrieveResponse:
+ """
+ Retrieve a framework document, including its sections, subsections, and the
+ rules mapped to each.
+
+ Each section and subsection carries a `ruleCount`, so you can tell which
+ requirements have rules mapped to them before drilling in.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ if not document_id:
+ raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}")
+ return self._get(
+ path_template(
+ "/frameworks/{framework_id}/documents/{document_id}", framework_id=framework_id, document_id=document_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DocumentRetrieveResponse,
+ )
+
+ def list(
+ self,
+ framework_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DocumentListResponse:
+ """
+ List the documents attached to a framework.
+
+ A document holds the text of the standard the framework is based on, split into
+ sections and subsections. Retrieve a single document to get that structure,
+ along with the rules mapped to each part of it.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._get(
+ path_template("/frameworks/{framework_id}/documents", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ document_list_params.DocumentListParams,
+ ),
+ ),
+ cast_to=DocumentListResponse,
+ )
+
+
+class AsyncDocumentsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncDocumentsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncDocumentsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncDocumentsResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ document_id: str,
+ *,
+ framework_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DocumentRetrieveResponse:
+ """
+ Retrieve a framework document, including its sections, subsections, and the
+ rules mapped to each.
+
+ Each section and subsection carries a `ruleCount`, so you can tell which
+ requirements have rules mapped to them before drilling in.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ if not document_id:
+ raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}")
+ return await self._get(
+ path_template(
+ "/frameworks/{framework_id}/documents/{document_id}", framework_id=framework_id, document_id=document_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DocumentRetrieveResponse,
+ )
+
+ async def list(
+ self,
+ framework_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DocumentListResponse:
+ """
+ List the documents attached to a framework.
+
+ A document holds the text of the standard the framework is based on, split into
+ sections and subsections. Retrieve a single document to get that structure,
+ along with the rules mapped to each part of it.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._get(
+ path_template("/frameworks/{framework_id}/documents", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ document_list_params.DocumentListParams,
+ ),
+ ),
+ cast_to=DocumentListResponse,
+ )
+
+
+class DocumentsResourceWithRawResponse:
+ def __init__(self, documents: DocumentsResource) -> None:
+ self._documents = documents
+
+ self.retrieve = to_raw_response_wrapper(
+ documents.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ documents.list,
+ )
+
+
+class AsyncDocumentsResourceWithRawResponse:
+ def __init__(self, documents: AsyncDocumentsResource) -> None:
+ self._documents = documents
+
+ self.retrieve = async_to_raw_response_wrapper(
+ documents.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ documents.list,
+ )
+
+
+class DocumentsResourceWithStreamingResponse:
+ def __init__(self, documents: DocumentsResource) -> None:
+ self._documents = documents
+
+ self.retrieve = to_streamed_response_wrapper(
+ documents.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ documents.list,
+ )
+
+
+class AsyncDocumentsResourceWithStreamingResponse:
+ def __init__(self, documents: AsyncDocumentsResource) -> None:
+ self._documents = documents
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ documents.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ documents.list,
+ )
diff --git a/src/openlayer/resources/governance/frameworks/frameworks.py b/src/openlayer/resources/governance/frameworks/frameworks.py
new file mode 100644
index 00000000..834eccbe
--- /dev/null
+++ b/src/openlayer/resources/governance/frameworks/frameworks.py
@@ -0,0 +1,1327 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+import httpx
+
+from .sections import (
+ SectionsResource,
+ AsyncSectionsResource,
+ SectionsResourceWithRawResponse,
+ AsyncSectionsResourceWithRawResponse,
+ SectionsResourceWithStreamingResponse,
+ AsyncSectionsResourceWithStreamingResponse,
+)
+from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from .documents import (
+ DocumentsResource,
+ AsyncDocumentsResource,
+ DocumentsResourceWithRawResponse,
+ AsyncDocumentsResourceWithRawResponse,
+ DocumentsResourceWithStreamingResponse,
+ AsyncDocumentsResourceWithStreamingResponse,
+)
+from ...._compat import cached_property
+from .subsections import (
+ SubsectionsResource,
+ AsyncSubsectionsResource,
+ SubsectionsResourceWithRawResponse,
+ AsyncSubsectionsResourceWithRawResponse,
+ SubsectionsResourceWithStreamingResponse,
+ AsyncSubsectionsResourceWithStreamingResponse,
+)
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.governance import (
+ framework_list_params,
+ framework_create_params,
+ framework_export_params,
+ framework_update_params,
+ framework_list_rules_params,
+ framework_list_projects_params,
+ framework_list_project_rule_stats_params,
+)
+from ....types.governance.framework_list_response import FrameworkListResponse
+from ....types.governance.framework_create_response import FrameworkCreateResponse
+from ....types.governance.framework_export_response import FrameworkExportResponse
+from ....types.governance.framework_update_response import FrameworkUpdateResponse
+from ....types.governance.framework_retrieve_response import FrameworkRetrieveResponse
+from ....types.governance.framework_list_rules_response import FrameworkListRulesResponse
+from ....types.governance.framework_list_projects_response import FrameworkListProjectsResponse
+from ....types.governance.framework_list_project_rule_stats_response import FrameworkListProjectRuleStatsResponse
+
+__all__ = ["FrameworksResource", "AsyncFrameworksResource"]
+
+
+class FrameworksResource(SyncAPIResource):
+ @cached_property
+ def documents(self) -> DocumentsResource:
+ return DocumentsResource(self._client)
+
+ @cached_property
+ def sections(self) -> SectionsResource:
+ return SectionsResource(self._client)
+
+ @cached_property
+ def subsections(self) -> SubsectionsResource:
+ return SubsectionsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> FrameworksResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return FrameworksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> FrameworksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return FrameworksResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ workspace_id: str,
+ *,
+ name: str,
+ description: Optional[str] | Omit = omit,
+ enabled: bool | Omit = omit,
+ project_selector: Optional[framework_create_params.ProjectSelector] | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkCreateResponse:
+ """
+ Create a custom governance framework in a workspace.
+
+ Use this to track compliance against an internal policy, or against a standard
+ Openlayer does not ship as a built-in framework. A new framework starts with no
+ rules -- add them from the Openlayer app, or map an existing rule to it.
+
+ A framework is created disabled unless you pass `enabled: true`. While it is
+ disabled its rules are not evaluated and do not count towards compliance.
+
+ Args:
+ name: The framework name.
+
+ description: A short description of the framework.
+
+ enabled: Whether the framework is active. Rules of a disabled framework are not evaluated
+ and do not count towards compliance.
+
+ project_selector: Determines which projects the framework applies to. An empty or `null` `match`
+ array applies the framework to every project in the workspace.
+
+ tags: Free-form labels on the framework.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._post(
+ path_template("/workspaces/{workspace_id}/frameworks", workspace_id=workspace_id),
+ body=maybe_transform(
+ {
+ "name": name,
+ "description": description,
+ "enabled": enabled,
+ "project_selector": project_selector,
+ "tags": tags,
+ },
+ framework_create_params.FrameworkCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ framework_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkRetrieveResponse:
+ """
+ Retrieve a governance framework by its id.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._get(
+ path_template("/frameworks/{framework_id}", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkRetrieveResponse,
+ )
+
+ def update(
+ self,
+ framework_id: str,
+ *,
+ avatar: Optional[framework_update_params.Avatar] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ enabled: bool | Omit = omit,
+ extended_description: Optional[Dict[str, object]] | Omit = omit,
+ href: Optional[str] | Omit = omit,
+ name: str | Omit = omit,
+ project_selector: Optional[framework_update_params.ProjectSelector] | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkUpdateResponse:
+ """
+ Update a governance framework.
+
+ The most common use is activating or deactivating a framework for the workspace
+ by setting `enabled`. Rules of a disabled framework are not evaluated and do not
+ count towards compliance.
+
+ Frameworks that ship with Openlayer report `immutable: true`. For those, only
+ `enabled`, `tags`, and `projectSelector` can be changed -- their name and
+ definition are managed by Openlayer.
+
+ Only the fields you send are changed.
+
+ Args:
+ avatar: The icon shown for the framework.
+
+ description: A short description of the framework.
+
+ enabled: Whether the framework is active. Rules of a disabled framework are not evaluated
+ and do not count towards compliance.
+
+ extended_description: A longer, rich-text description, as a TipTap JSON document.
+
+ href: A link to the external standard or regulation the framework is based on.
+
+ name: The framework name.
+
+ project_selector: Determines which projects the framework applies to. An empty or `null` `match`
+ array applies the framework to every project in the workspace.
+
+ tags: Free-form labels on the framework.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._put(
+ path_template("/frameworks/{framework_id}", framework_id=framework_id),
+ body=maybe_transform(
+ {
+ "avatar": avatar,
+ "description": description,
+ "enabled": enabled,
+ "extended_description": extended_description,
+ "href": href,
+ "name": name,
+ "project_selector": project_selector,
+ "tags": tags,
+ },
+ framework_update_params.FrameworkUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkUpdateResponse,
+ )
+
+ def list(
+ self,
+ workspace_id: str,
+ *,
+ asc: bool | Omit = omit,
+ completion_operator: Literal["is", ">", ">=", "<", "<=", "!="] | Omit = omit,
+ completion_value: int | Omit = omit,
+ enabled: bool | Omit = omit,
+ include_rule_stats: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ search_query: str | Omit = omit,
+ sort_column: Literal[
+ "name", "enabled", "dateCreated", "dateUpdated", "overallCompletion", "projectCompletionBuckets"
+ ]
+ | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListResponse:
+ """
+ List the governance frameworks in a workspace.
+
+ A framework is a set of rules -- drawn from a regulation, a standard, or your
+ own internal policy -- that Openlayer tracks compliance against. Use this
+ endpoint to find the framework you want to report on, then read its rules and
+ rule results.
+
+ Args:
+ asc: Whether to sort in ascending order.
+
+ completion_operator: How to compare each framework's completion percentage with `completionValue`.
+ Must be sent together with `completionValue`.
+
+ completion_value: The completion percentage to compare against, from 0 to 100.
+
+ enabled: Only include frameworks that are enabled (or disabled).
+
+ include_rule_stats: Whether to include a `ruleStats` object on each framework, with its rule result
+ status counts and its per-project completion buckets. Computed over the returned
+ page only.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ search_query: Filter by a free-text search over names and descriptions.
+
+ sort_column: The column to sort on.
+
+ tags: Only include frameworks carrying all of these tags.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._get(
+ path_template("/workspaces/{workspace_id}/frameworks", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "asc": asc,
+ "completion_operator": completion_operator,
+ "completion_value": completion_value,
+ "enabled": enabled,
+ "include_rule_stats": include_rule_stats,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "search_query": search_query,
+ "sort_column": sort_column,
+ "tags": tags,
+ },
+ framework_list_params.FrameworkListParams,
+ ),
+ ),
+ cast_to=FrameworkListResponse,
+ )
+
+ def export(
+ self,
+ framework_id: str,
+ *,
+ project_id: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkExportResponse:
+ """
+ Export a framework's evidence and progress as an audit-ready zip archive.
+
+ The archive holds every evidence file uploaded against the framework's
+ evidence-based rules, a markdown report of the framework's progress and the
+ status of all its rules (broken down by documentation section when the framework
+ has documents), and CSV manifests of rules and evidence with SHA-256 checksums.
+
+ Send `projectId` to export one project's compliance with the framework. Omit it
+ for the workspace-wide view across every project in the framework, including
+ workspace-scoped rules.
+
+ The export runs as a background task, so this returns `202` immediately. To
+ collect the archive:
+
+ 1. Poll `GET /background-tasks/{taskId}` with the returned `taskResultId` until
+ `complete` is `true`.
+ 2. Read `outputs.storageUri` off that task.
+ 3. Exchange it for a download link at
+ `GET /storage/presigned-url?storageUri=`.
+
+ Rate limited to 2 requests per minute per framework. Asking for an export while
+ an identical one is still queued returns that task rather than starting a second
+ one.
+
+ Args:
+ project_id: Scope the export to this project. It must belong to the framework.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._post(
+ path_template("/frameworks/{framework_id}/export", framework_id=framework_id),
+ body=maybe_transform({"project_id": project_id}, framework_export_params.FrameworkExportParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkExportResponse,
+ )
+
+ def list_project_rule_stats(
+ self,
+ framework_id: str,
+ *,
+ asc: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ sort_column: Literal[
+ "projectName",
+ "total",
+ "overallCompletion",
+ "totalPassing",
+ "totalFailing",
+ "totalSkipped",
+ "totalRunning",
+ "totalError",
+ "totalPending",
+ "totalDueSoon",
+ ]
+ | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListProjectRuleStatsResponse:
+ """
+ Get a compliance roll-up for a framework, one row per project it applies to.
+
+ Each row counts the project's rule results by status, so you can report on where
+ a framework is complete and where it is not without fetching every individual
+ rule result.
+
+ Args:
+ asc: Whether to sort in ascending order.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ sort_column: The column to sort on.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._get(
+ path_template("/frameworks/{framework_id}/project-rule-stats", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "asc": asc,
+ "page": page,
+ "per_page": per_page,
+ "sort_column": sort_column,
+ },
+ framework_list_project_rule_stats_params.FrameworkListProjectRuleStatsParams,
+ ),
+ ),
+ cast_to=FrameworkListProjectRuleStatsResponse,
+ )
+
+ def list_projects(
+ self,
+ framework_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListProjectsResponse:
+ """
+ List the projects a framework applies to.
+
+ Which projects a framework covers is determined by its `projectSelector`. A
+ framework with an empty selector applies to every project in the workspace.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._get(
+ path_template("/frameworks/{framework_id}/projects", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ framework_list_projects_params.FrameworkListProjectsParams,
+ ),
+ ),
+ cast_to=FrameworkListProjectsResponse,
+ )
+
+ def list_rules(
+ self,
+ framework_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListRulesResponse:
+ """
+ List the rules that belong to a framework.
+
+ To read the compliance status of these rules, use
+ [List rule results](/api-reference/rest/governance/list-rule-results) with the
+ `frameworkId` filter, or fetch the results of an individual rule.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return self._get(
+ path_template("/frameworks/{framework_id}/rules", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ framework_list_rules_params.FrameworkListRulesParams,
+ ),
+ ),
+ cast_to=FrameworkListRulesResponse,
+ )
+
+
+class AsyncFrameworksResource(AsyncAPIResource):
+ @cached_property
+ def documents(self) -> AsyncDocumentsResource:
+ return AsyncDocumentsResource(self._client)
+
+ @cached_property
+ def sections(self) -> AsyncSectionsResource:
+ return AsyncSectionsResource(self._client)
+
+ @cached_property
+ def subsections(self) -> AsyncSubsectionsResource:
+ return AsyncSubsectionsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncFrameworksResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncFrameworksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncFrameworksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncFrameworksResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ workspace_id: str,
+ *,
+ name: str,
+ description: Optional[str] | Omit = omit,
+ enabled: bool | Omit = omit,
+ project_selector: Optional[framework_create_params.ProjectSelector] | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkCreateResponse:
+ """
+ Create a custom governance framework in a workspace.
+
+ Use this to track compliance against an internal policy, or against a standard
+ Openlayer does not ship as a built-in framework. A new framework starts with no
+ rules -- add them from the Openlayer app, or map an existing rule to it.
+
+ A framework is created disabled unless you pass `enabled: true`. While it is
+ disabled its rules are not evaluated and do not count towards compliance.
+
+ Args:
+ name: The framework name.
+
+ description: A short description of the framework.
+
+ enabled: Whether the framework is active. Rules of a disabled framework are not evaluated
+ and do not count towards compliance.
+
+ project_selector: Determines which projects the framework applies to. An empty or `null` `match`
+ array applies the framework to every project in the workspace.
+
+ tags: Free-form labels on the framework.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._post(
+ path_template("/workspaces/{workspace_id}/frameworks", workspace_id=workspace_id),
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "description": description,
+ "enabled": enabled,
+ "project_selector": project_selector,
+ "tags": tags,
+ },
+ framework_create_params.FrameworkCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ framework_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkRetrieveResponse:
+ """
+ Retrieve a governance framework by its id.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._get(
+ path_template("/frameworks/{framework_id}", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ framework_id: str,
+ *,
+ avatar: Optional[framework_update_params.Avatar] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ enabled: bool | Omit = omit,
+ extended_description: Optional[Dict[str, object]] | Omit = omit,
+ href: Optional[str] | Omit = omit,
+ name: str | Omit = omit,
+ project_selector: Optional[framework_update_params.ProjectSelector] | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkUpdateResponse:
+ """
+ Update a governance framework.
+
+ The most common use is activating or deactivating a framework for the workspace
+ by setting `enabled`. Rules of a disabled framework are not evaluated and do not
+ count towards compliance.
+
+ Frameworks that ship with Openlayer report `immutable: true`. For those, only
+ `enabled`, `tags`, and `projectSelector` can be changed -- their name and
+ definition are managed by Openlayer.
+
+ Only the fields you send are changed.
+
+ Args:
+ avatar: The icon shown for the framework.
+
+ description: A short description of the framework.
+
+ enabled: Whether the framework is active. Rules of a disabled framework are not evaluated
+ and do not count towards compliance.
+
+ extended_description: A longer, rich-text description, as a TipTap JSON document.
+
+ href: A link to the external standard or regulation the framework is based on.
+
+ name: The framework name.
+
+ project_selector: Determines which projects the framework applies to. An empty or `null` `match`
+ array applies the framework to every project in the workspace.
+
+ tags: Free-form labels on the framework.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._put(
+ path_template("/frameworks/{framework_id}", framework_id=framework_id),
+ body=await async_maybe_transform(
+ {
+ "avatar": avatar,
+ "description": description,
+ "enabled": enabled,
+ "extended_description": extended_description,
+ "href": href,
+ "name": name,
+ "project_selector": project_selector,
+ "tags": tags,
+ },
+ framework_update_params.FrameworkUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkUpdateResponse,
+ )
+
+ async def list(
+ self,
+ workspace_id: str,
+ *,
+ asc: bool | Omit = omit,
+ completion_operator: Literal["is", ">", ">=", "<", "<=", "!="] | Omit = omit,
+ completion_value: int | Omit = omit,
+ enabled: bool | Omit = omit,
+ include_rule_stats: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ search_query: str | Omit = omit,
+ sort_column: Literal[
+ "name", "enabled", "dateCreated", "dateUpdated", "overallCompletion", "projectCompletionBuckets"
+ ]
+ | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListResponse:
+ """
+ List the governance frameworks in a workspace.
+
+ A framework is a set of rules -- drawn from a regulation, a standard, or your
+ own internal policy -- that Openlayer tracks compliance against. Use this
+ endpoint to find the framework you want to report on, then read its rules and
+ rule results.
+
+ Args:
+ asc: Whether to sort in ascending order.
+
+ completion_operator: How to compare each framework's completion percentage with `completionValue`.
+ Must be sent together with `completionValue`.
+
+ completion_value: The completion percentage to compare against, from 0 to 100.
+
+ enabled: Only include frameworks that are enabled (or disabled).
+
+ include_rule_stats: Whether to include a `ruleStats` object on each framework, with its rule result
+ status counts and its per-project completion buckets. Computed over the returned
+ page only.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ search_query: Filter by a free-text search over names and descriptions.
+
+ sort_column: The column to sort on.
+
+ tags: Only include frameworks carrying all of these tags.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._get(
+ path_template("/workspaces/{workspace_id}/frameworks", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "asc": asc,
+ "completion_operator": completion_operator,
+ "completion_value": completion_value,
+ "enabled": enabled,
+ "include_rule_stats": include_rule_stats,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "search_query": search_query,
+ "sort_column": sort_column,
+ "tags": tags,
+ },
+ framework_list_params.FrameworkListParams,
+ ),
+ ),
+ cast_to=FrameworkListResponse,
+ )
+
+ async def export(
+ self,
+ framework_id: str,
+ *,
+ project_id: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkExportResponse:
+ """
+ Export a framework's evidence and progress as an audit-ready zip archive.
+
+ The archive holds every evidence file uploaded against the framework's
+ evidence-based rules, a markdown report of the framework's progress and the
+ status of all its rules (broken down by documentation section when the framework
+ has documents), and CSV manifests of rules and evidence with SHA-256 checksums.
+
+ Send `projectId` to export one project's compliance with the framework. Omit it
+ for the workspace-wide view across every project in the framework, including
+ workspace-scoped rules.
+
+ The export runs as a background task, so this returns `202` immediately. To
+ collect the archive:
+
+ 1. Poll `GET /background-tasks/{taskId}` with the returned `taskResultId` until
+ `complete` is `true`.
+ 2. Read `outputs.storageUri` off that task.
+ 3. Exchange it for a download link at
+ `GET /storage/presigned-url?storageUri=`.
+
+ Rate limited to 2 requests per minute per framework. Asking for an export while
+ an identical one is still queued returns that task rather than starting a second
+ one.
+
+ Args:
+ project_id: Scope the export to this project. It must belong to the framework.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._post(
+ path_template("/frameworks/{framework_id}/export", framework_id=framework_id),
+ body=await async_maybe_transform({"project_id": project_id}, framework_export_params.FrameworkExportParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FrameworkExportResponse,
+ )
+
+ async def list_project_rule_stats(
+ self,
+ framework_id: str,
+ *,
+ asc: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ sort_column: Literal[
+ "projectName",
+ "total",
+ "overallCompletion",
+ "totalPassing",
+ "totalFailing",
+ "totalSkipped",
+ "totalRunning",
+ "totalError",
+ "totalPending",
+ "totalDueSoon",
+ ]
+ | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListProjectRuleStatsResponse:
+ """
+ Get a compliance roll-up for a framework, one row per project it applies to.
+
+ Each row counts the project's rule results by status, so you can report on where
+ a framework is complete and where it is not without fetching every individual
+ rule result.
+
+ Args:
+ asc: Whether to sort in ascending order.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ sort_column: The column to sort on.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._get(
+ path_template("/frameworks/{framework_id}/project-rule-stats", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "asc": asc,
+ "page": page,
+ "per_page": per_page,
+ "sort_column": sort_column,
+ },
+ framework_list_project_rule_stats_params.FrameworkListProjectRuleStatsParams,
+ ),
+ ),
+ cast_to=FrameworkListProjectRuleStatsResponse,
+ )
+
+ async def list_projects(
+ self,
+ framework_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListProjectsResponse:
+ """
+ List the projects a framework applies to.
+
+ Which projects a framework covers is determined by its `projectSelector`. A
+ framework with an empty selector applies to every project in the workspace.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._get(
+ path_template("/frameworks/{framework_id}/projects", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ framework_list_projects_params.FrameworkListProjectsParams,
+ ),
+ ),
+ cast_to=FrameworkListProjectsResponse,
+ )
+
+ async def list_rules(
+ self,
+ framework_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FrameworkListRulesResponse:
+ """
+ List the rules that belong to a framework.
+
+ To read the compliance status of these rules, use
+ [List rule results](/api-reference/rest/governance/list-rule-results) with the
+ `frameworkId` filter, or fetch the results of an individual rule.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ return await self._get(
+ path_template("/frameworks/{framework_id}/rules", framework_id=framework_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ framework_list_rules_params.FrameworkListRulesParams,
+ ),
+ ),
+ cast_to=FrameworkListRulesResponse,
+ )
+
+
+class FrameworksResourceWithRawResponse:
+ def __init__(self, frameworks: FrameworksResource) -> None:
+ self._frameworks = frameworks
+
+ self.create = to_raw_response_wrapper(
+ frameworks.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ frameworks.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ frameworks.update,
+ )
+ self.list = to_raw_response_wrapper(
+ frameworks.list,
+ )
+ self.export = to_raw_response_wrapper(
+ frameworks.export,
+ )
+ self.list_project_rule_stats = to_raw_response_wrapper(
+ frameworks.list_project_rule_stats,
+ )
+ self.list_projects = to_raw_response_wrapper(
+ frameworks.list_projects,
+ )
+ self.list_rules = to_raw_response_wrapper(
+ frameworks.list_rules,
+ )
+
+ @cached_property
+ def documents(self) -> DocumentsResourceWithRawResponse:
+ return DocumentsResourceWithRawResponse(self._frameworks.documents)
+
+ @cached_property
+ def sections(self) -> SectionsResourceWithRawResponse:
+ return SectionsResourceWithRawResponse(self._frameworks.sections)
+
+ @cached_property
+ def subsections(self) -> SubsectionsResourceWithRawResponse:
+ return SubsectionsResourceWithRawResponse(self._frameworks.subsections)
+
+
+class AsyncFrameworksResourceWithRawResponse:
+ def __init__(self, frameworks: AsyncFrameworksResource) -> None:
+ self._frameworks = frameworks
+
+ self.create = async_to_raw_response_wrapper(
+ frameworks.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ frameworks.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ frameworks.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ frameworks.list,
+ )
+ self.export = async_to_raw_response_wrapper(
+ frameworks.export,
+ )
+ self.list_project_rule_stats = async_to_raw_response_wrapper(
+ frameworks.list_project_rule_stats,
+ )
+ self.list_projects = async_to_raw_response_wrapper(
+ frameworks.list_projects,
+ )
+ self.list_rules = async_to_raw_response_wrapper(
+ frameworks.list_rules,
+ )
+
+ @cached_property
+ def documents(self) -> AsyncDocumentsResourceWithRawResponse:
+ return AsyncDocumentsResourceWithRawResponse(self._frameworks.documents)
+
+ @cached_property
+ def sections(self) -> AsyncSectionsResourceWithRawResponse:
+ return AsyncSectionsResourceWithRawResponse(self._frameworks.sections)
+
+ @cached_property
+ def subsections(self) -> AsyncSubsectionsResourceWithRawResponse:
+ return AsyncSubsectionsResourceWithRawResponse(self._frameworks.subsections)
+
+
+class FrameworksResourceWithStreamingResponse:
+ def __init__(self, frameworks: FrameworksResource) -> None:
+ self._frameworks = frameworks
+
+ self.create = to_streamed_response_wrapper(
+ frameworks.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ frameworks.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ frameworks.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ frameworks.list,
+ )
+ self.export = to_streamed_response_wrapper(
+ frameworks.export,
+ )
+ self.list_project_rule_stats = to_streamed_response_wrapper(
+ frameworks.list_project_rule_stats,
+ )
+ self.list_projects = to_streamed_response_wrapper(
+ frameworks.list_projects,
+ )
+ self.list_rules = to_streamed_response_wrapper(
+ frameworks.list_rules,
+ )
+
+ @cached_property
+ def documents(self) -> DocumentsResourceWithStreamingResponse:
+ return DocumentsResourceWithStreamingResponse(self._frameworks.documents)
+
+ @cached_property
+ def sections(self) -> SectionsResourceWithStreamingResponse:
+ return SectionsResourceWithStreamingResponse(self._frameworks.sections)
+
+ @cached_property
+ def subsections(self) -> SubsectionsResourceWithStreamingResponse:
+ return SubsectionsResourceWithStreamingResponse(self._frameworks.subsections)
+
+
+class AsyncFrameworksResourceWithStreamingResponse:
+ def __init__(self, frameworks: AsyncFrameworksResource) -> None:
+ self._frameworks = frameworks
+
+ self.create = async_to_streamed_response_wrapper(
+ frameworks.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ frameworks.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ frameworks.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ frameworks.list,
+ )
+ self.export = async_to_streamed_response_wrapper(
+ frameworks.export,
+ )
+ self.list_project_rule_stats = async_to_streamed_response_wrapper(
+ frameworks.list_project_rule_stats,
+ )
+ self.list_projects = async_to_streamed_response_wrapper(
+ frameworks.list_projects,
+ )
+ self.list_rules = async_to_streamed_response_wrapper(
+ frameworks.list_rules,
+ )
+
+ @cached_property
+ def documents(self) -> AsyncDocumentsResourceWithStreamingResponse:
+ return AsyncDocumentsResourceWithStreamingResponse(self._frameworks.documents)
+
+ @cached_property
+ def sections(self) -> AsyncSectionsResourceWithStreamingResponse:
+ return AsyncSectionsResourceWithStreamingResponse(self._frameworks.sections)
+
+ @cached_property
+ def subsections(self) -> AsyncSubsectionsResourceWithStreamingResponse:
+ return AsyncSubsectionsResourceWithStreamingResponse(self._frameworks.subsections)
diff --git a/src/openlayer/resources/governance/frameworks/sections.py b/src/openlayer/resources/governance/frameworks/sections.py
new file mode 100644
index 00000000..7cfcb382
--- /dev/null
+++ b/src/openlayer/resources/governance/frameworks/sections.py
@@ -0,0 +1,253 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.governance.frameworks import section_list_rules_params
+from ....types.governance.frameworks.section_list_rules_response import SectionListRulesResponse
+
+__all__ = ["SectionsResource", "AsyncSectionsResource"]
+
+
+class SectionsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> SectionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return SectionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SectionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return SectionsResourceWithStreamingResponse(self)
+
+ def list_rules(
+ self,
+ section_id: str,
+ *,
+ framework_id: str,
+ include_results: bool | Omit = omit,
+ include_subsection_rules: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SectionListRulesResponse:
+ """
+ List the rules mapped to a section of a framework document.
+
+ Pass `includeSubsectionRules=true` to also return the rules mapped to the
+ section's subsections, which is how you get every rule covering a requirement
+ and everything under it.
+
+ Args:
+ include_results: Whether to include each rule's results inline, in a `results` array.
+
+ include_subsection_rules: Whether to also include the rules mapped to the section's subsections.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ status: Only include items whose rule result has this compliance status.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ if not section_id:
+ raise ValueError(f"Expected a non-empty value for `section_id` but received {section_id!r}")
+ return self._get(
+ path_template(
+ "/frameworks/{framework_id}/sections/{section_id}/rules",
+ framework_id=framework_id,
+ section_id=section_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "include_results": include_results,
+ "include_subsection_rules": include_subsection_rules,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "status": status,
+ },
+ section_list_rules_params.SectionListRulesParams,
+ ),
+ ),
+ cast_to=SectionListRulesResponse,
+ )
+
+
+class AsyncSectionsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncSectionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSectionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSectionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncSectionsResourceWithStreamingResponse(self)
+
+ async def list_rules(
+ self,
+ section_id: str,
+ *,
+ framework_id: str,
+ include_results: bool | Omit = omit,
+ include_subsection_rules: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SectionListRulesResponse:
+ """
+ List the rules mapped to a section of a framework document.
+
+ Pass `includeSubsectionRules=true` to also return the rules mapped to the
+ section's subsections, which is how you get every rule covering a requirement
+ and everything under it.
+
+ Args:
+ include_results: Whether to include each rule's results inline, in a `results` array.
+
+ include_subsection_rules: Whether to also include the rules mapped to the section's subsections.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ status: Only include items whose rule result has this compliance status.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ if not section_id:
+ raise ValueError(f"Expected a non-empty value for `section_id` but received {section_id!r}")
+ return await self._get(
+ path_template(
+ "/frameworks/{framework_id}/sections/{section_id}/rules",
+ framework_id=framework_id,
+ section_id=section_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "include_results": include_results,
+ "include_subsection_rules": include_subsection_rules,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "status": status,
+ },
+ section_list_rules_params.SectionListRulesParams,
+ ),
+ ),
+ cast_to=SectionListRulesResponse,
+ )
+
+
+class SectionsResourceWithRawResponse:
+ def __init__(self, sections: SectionsResource) -> None:
+ self._sections = sections
+
+ self.list_rules = to_raw_response_wrapper(
+ sections.list_rules,
+ )
+
+
+class AsyncSectionsResourceWithRawResponse:
+ def __init__(self, sections: AsyncSectionsResource) -> None:
+ self._sections = sections
+
+ self.list_rules = async_to_raw_response_wrapper(
+ sections.list_rules,
+ )
+
+
+class SectionsResourceWithStreamingResponse:
+ def __init__(self, sections: SectionsResource) -> None:
+ self._sections = sections
+
+ self.list_rules = to_streamed_response_wrapper(
+ sections.list_rules,
+ )
+
+
+class AsyncSectionsResourceWithStreamingResponse:
+ def __init__(self, sections: AsyncSectionsResource) -> None:
+ self._sections = sections
+
+ self.list_rules = async_to_streamed_response_wrapper(
+ sections.list_rules,
+ )
diff --git a/src/openlayer/resources/governance/frameworks/subsections.py b/src/openlayer/resources/governance/frameworks/subsections.py
new file mode 100644
index 00000000..564325ce
--- /dev/null
+++ b/src/openlayer/resources/governance/frameworks/subsections.py
@@ -0,0 +1,245 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.governance.frameworks import subsection_list_rules_params
+from ....types.governance.frameworks.subsection_list_rules_response import SubsectionListRulesResponse
+
+__all__ = ["SubsectionsResource", "AsyncSubsectionsResource"]
+
+
+class SubsectionsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> SubsectionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return SubsectionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SubsectionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return SubsectionsResourceWithStreamingResponse(self)
+
+ def list_rules(
+ self,
+ subsection_id: str,
+ *,
+ framework_id: str,
+ include_results: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SubsectionListRulesResponse:
+ """
+ List the rules mapped to a subsection of a framework document.
+
+ A subsection is usually the level at which a standard states an individual
+ requirement, so this is the endpoint to use when you want to show which rules
+ cover a specific clause.
+
+ Args:
+ include_results: Whether to include each rule's results inline, in a `results` array.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ status: Only include items whose rule result has this compliance status.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ if not subsection_id:
+ raise ValueError(f"Expected a non-empty value for `subsection_id` but received {subsection_id!r}")
+ return self._get(
+ path_template(
+ "/frameworks/{framework_id}/subsections/{subsection_id}/rules",
+ framework_id=framework_id,
+ subsection_id=subsection_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "include_results": include_results,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "status": status,
+ },
+ subsection_list_rules_params.SubsectionListRulesParams,
+ ),
+ ),
+ cast_to=SubsectionListRulesResponse,
+ )
+
+
+class AsyncSubsectionsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncSubsectionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSubsectionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSubsectionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncSubsectionsResourceWithStreamingResponse(self)
+
+ async def list_rules(
+ self,
+ subsection_id: str,
+ *,
+ framework_id: str,
+ include_results: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SubsectionListRulesResponse:
+ """
+ List the rules mapped to a subsection of a framework document.
+
+ A subsection is usually the level at which a standard states an individual
+ requirement, so this is the endpoint to use when you want to show which rules
+ cover a specific clause.
+
+ Args:
+ include_results: Whether to include each rule's results inline, in a `results` array.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ status: Only include items whose rule result has this compliance status.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not framework_id:
+ raise ValueError(f"Expected a non-empty value for `framework_id` but received {framework_id!r}")
+ if not subsection_id:
+ raise ValueError(f"Expected a non-empty value for `subsection_id` but received {subsection_id!r}")
+ return await self._get(
+ path_template(
+ "/frameworks/{framework_id}/subsections/{subsection_id}/rules",
+ framework_id=framework_id,
+ subsection_id=subsection_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "include_results": include_results,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "status": status,
+ },
+ subsection_list_rules_params.SubsectionListRulesParams,
+ ),
+ ),
+ cast_to=SubsectionListRulesResponse,
+ )
+
+
+class SubsectionsResourceWithRawResponse:
+ def __init__(self, subsections: SubsectionsResource) -> None:
+ self._subsections = subsections
+
+ self.list_rules = to_raw_response_wrapper(
+ subsections.list_rules,
+ )
+
+
+class AsyncSubsectionsResourceWithRawResponse:
+ def __init__(self, subsections: AsyncSubsectionsResource) -> None:
+ self._subsections = subsections
+
+ self.list_rules = async_to_raw_response_wrapper(
+ subsections.list_rules,
+ )
+
+
+class SubsectionsResourceWithStreamingResponse:
+ def __init__(self, subsections: SubsectionsResource) -> None:
+ self._subsections = subsections
+
+ self.list_rules = to_streamed_response_wrapper(
+ subsections.list_rules,
+ )
+
+
+class AsyncSubsectionsResourceWithStreamingResponse:
+ def __init__(self, subsections: AsyncSubsectionsResource) -> None:
+ self._subsections = subsections
+
+ self.list_rules = async_to_streamed_response_wrapper(
+ subsections.list_rules,
+ )
diff --git a/src/openlayer/resources/governance/governance.py b/src/openlayer/resources/governance/governance.py
new file mode 100644
index 00000000..c5c82818
--- /dev/null
+++ b/src/openlayer/resources/governance/governance.py
@@ -0,0 +1,230 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .rules import (
+ RulesResource,
+ AsyncRulesResource,
+ RulesResourceWithRawResponse,
+ AsyncRulesResourceWithRawResponse,
+ RulesResourceWithStreamingResponse,
+ AsyncRulesResourceWithStreamingResponse,
+)
+from ..._compat import cached_property
+from .rule_tags import (
+ RuleTagsResource,
+ AsyncRuleTagsResource,
+ RuleTagsResourceWithRawResponse,
+ AsyncRuleTagsResourceWithRawResponse,
+ RuleTagsResourceWithStreamingResponse,
+ AsyncRuleTagsResourceWithStreamingResponse,
+)
+from .rule_stats import (
+ RuleStatsResource,
+ AsyncRuleStatsResource,
+ RuleStatsResourceWithRawResponse,
+ AsyncRuleStatsResourceWithRawResponse,
+ RuleStatsResourceWithStreamingResponse,
+ AsyncRuleStatsResourceWithStreamingResponse,
+)
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from .rule_results import (
+ RuleResultsResource,
+ AsyncRuleResultsResource,
+ RuleResultsResourceWithRawResponse,
+ AsyncRuleResultsResourceWithRawResponse,
+ RuleResultsResourceWithStreamingResponse,
+ AsyncRuleResultsResourceWithStreamingResponse,
+)
+from .frameworks.frameworks import (
+ FrameworksResource,
+ AsyncFrameworksResource,
+ FrameworksResourceWithRawResponse,
+ AsyncFrameworksResourceWithRawResponse,
+ FrameworksResourceWithStreamingResponse,
+ AsyncFrameworksResourceWithStreamingResponse,
+)
+
+__all__ = ["GovernanceResource", "AsyncGovernanceResource"]
+
+
+class GovernanceResource(SyncAPIResource):
+ @cached_property
+ def frameworks(self) -> FrameworksResource:
+ return FrameworksResource(self._client)
+
+ @cached_property
+ def rules(self) -> RulesResource:
+ return RulesResource(self._client)
+
+ @cached_property
+ def rule_results(self) -> RuleResultsResource:
+ return RuleResultsResource(self._client)
+
+ @cached_property
+ def rule_stats(self) -> RuleStatsResource:
+ return RuleStatsResource(self._client)
+
+ @cached_property
+ def rule_tags(self) -> RuleTagsResource:
+ return RuleTagsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> GovernanceResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return GovernanceResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> GovernanceResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return GovernanceResourceWithStreamingResponse(self)
+
+
+class AsyncGovernanceResource(AsyncAPIResource):
+ @cached_property
+ def frameworks(self) -> AsyncFrameworksResource:
+ return AsyncFrameworksResource(self._client)
+
+ @cached_property
+ def rules(self) -> AsyncRulesResource:
+ return AsyncRulesResource(self._client)
+
+ @cached_property
+ def rule_results(self) -> AsyncRuleResultsResource:
+ return AsyncRuleResultsResource(self._client)
+
+ @cached_property
+ def rule_stats(self) -> AsyncRuleStatsResource:
+ return AsyncRuleStatsResource(self._client)
+
+ @cached_property
+ def rule_tags(self) -> AsyncRuleTagsResource:
+ return AsyncRuleTagsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncGovernanceResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncGovernanceResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncGovernanceResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncGovernanceResourceWithStreamingResponse(self)
+
+
+class GovernanceResourceWithRawResponse:
+ def __init__(self, governance: GovernanceResource) -> None:
+ self._governance = governance
+
+ @cached_property
+ def frameworks(self) -> FrameworksResourceWithRawResponse:
+ return FrameworksResourceWithRawResponse(self._governance.frameworks)
+
+ @cached_property
+ def rules(self) -> RulesResourceWithRawResponse:
+ return RulesResourceWithRawResponse(self._governance.rules)
+
+ @cached_property
+ def rule_results(self) -> RuleResultsResourceWithRawResponse:
+ return RuleResultsResourceWithRawResponse(self._governance.rule_results)
+
+ @cached_property
+ def rule_stats(self) -> RuleStatsResourceWithRawResponse:
+ return RuleStatsResourceWithRawResponse(self._governance.rule_stats)
+
+ @cached_property
+ def rule_tags(self) -> RuleTagsResourceWithRawResponse:
+ return RuleTagsResourceWithRawResponse(self._governance.rule_tags)
+
+
+class AsyncGovernanceResourceWithRawResponse:
+ def __init__(self, governance: AsyncGovernanceResource) -> None:
+ self._governance = governance
+
+ @cached_property
+ def frameworks(self) -> AsyncFrameworksResourceWithRawResponse:
+ return AsyncFrameworksResourceWithRawResponse(self._governance.frameworks)
+
+ @cached_property
+ def rules(self) -> AsyncRulesResourceWithRawResponse:
+ return AsyncRulesResourceWithRawResponse(self._governance.rules)
+
+ @cached_property
+ def rule_results(self) -> AsyncRuleResultsResourceWithRawResponse:
+ return AsyncRuleResultsResourceWithRawResponse(self._governance.rule_results)
+
+ @cached_property
+ def rule_stats(self) -> AsyncRuleStatsResourceWithRawResponse:
+ return AsyncRuleStatsResourceWithRawResponse(self._governance.rule_stats)
+
+ @cached_property
+ def rule_tags(self) -> AsyncRuleTagsResourceWithRawResponse:
+ return AsyncRuleTagsResourceWithRawResponse(self._governance.rule_tags)
+
+
+class GovernanceResourceWithStreamingResponse:
+ def __init__(self, governance: GovernanceResource) -> None:
+ self._governance = governance
+
+ @cached_property
+ def frameworks(self) -> FrameworksResourceWithStreamingResponse:
+ return FrameworksResourceWithStreamingResponse(self._governance.frameworks)
+
+ @cached_property
+ def rules(self) -> RulesResourceWithStreamingResponse:
+ return RulesResourceWithStreamingResponse(self._governance.rules)
+
+ @cached_property
+ def rule_results(self) -> RuleResultsResourceWithStreamingResponse:
+ return RuleResultsResourceWithStreamingResponse(self._governance.rule_results)
+
+ @cached_property
+ def rule_stats(self) -> RuleStatsResourceWithStreamingResponse:
+ return RuleStatsResourceWithStreamingResponse(self._governance.rule_stats)
+
+ @cached_property
+ def rule_tags(self) -> RuleTagsResourceWithStreamingResponse:
+ return RuleTagsResourceWithStreamingResponse(self._governance.rule_tags)
+
+
+class AsyncGovernanceResourceWithStreamingResponse:
+ def __init__(self, governance: AsyncGovernanceResource) -> None:
+ self._governance = governance
+
+ @cached_property
+ def frameworks(self) -> AsyncFrameworksResourceWithStreamingResponse:
+ return AsyncFrameworksResourceWithStreamingResponse(self._governance.frameworks)
+
+ @cached_property
+ def rules(self) -> AsyncRulesResourceWithStreamingResponse:
+ return AsyncRulesResourceWithStreamingResponse(self._governance.rules)
+
+ @cached_property
+ def rule_results(self) -> AsyncRuleResultsResourceWithStreamingResponse:
+ return AsyncRuleResultsResourceWithStreamingResponse(self._governance.rule_results)
+
+ @cached_property
+ def rule_stats(self) -> AsyncRuleStatsResourceWithStreamingResponse:
+ return AsyncRuleStatsResourceWithStreamingResponse(self._governance.rule_stats)
+
+ @cached_property
+ def rule_tags(self) -> AsyncRuleTagsResourceWithStreamingResponse:
+ return AsyncRuleTagsResourceWithStreamingResponse(self._governance.rule_tags)
diff --git a/src/openlayer/resources/governance/rule_results.py b/src/openlayer/resources/governance/rule_results.py
new file mode 100644
index 00000000..563457aa
--- /dev/null
+++ b/src/openlayer/resources/governance/rule_results.py
@@ -0,0 +1,785 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.governance import (
+ rule_result_list_params,
+ rule_result_update_params,
+ rule_result_list_evidence_params,
+ rule_result_create_evidence_params,
+)
+from ...types.governance.rule_result_list_response import RuleResultListResponse
+from ...types.governance.rule_result_update_response import RuleResultUpdateResponse
+from ...types.governance.rule_result_retrieve_response import RuleResultRetrieveResponse
+from ...types.governance.rule_result_list_evidence_response import RuleResultListEvidenceResponse
+from ...types.governance.rule_result_create_evidence_response import RuleResultCreateEvidenceResponse
+
+__all__ = ["RuleResultsResource", "AsyncRuleResultsResource"]
+
+
+class RuleResultsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> RuleResultsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return RuleResultsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> RuleResultsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return RuleResultsResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ rule_result_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultRetrieveResponse:
+ """
+ Retrieve a rule result by its id.
+
+ Alongside the status, the response carries the evaluation and renewal dates that
+ explain it: `dateLastEvaluated` and `dateOfNextEvaluation` for platform rules,
+ `dateOfLatestEvidence` and `dateOfRenewal` for evidence rules.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return self._get(
+ path_template("/rule-results/{rule_result_id}", rule_result_id=rule_result_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleResultRetrieveResponse,
+ )
+
+ def update(
+ self,
+ rule_result_id: str,
+ *,
+ assignee_id: Optional[str] | Omit = omit,
+ blocked_by: Iterable[rule_result_update_params.BlockedBy] | Omit = omit,
+ blocking: Iterable[rule_result_update_params.Blocking] | Omit = omit,
+ deactivated: bool | Omit = omit,
+ deactivated_reason: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultUpdateResponse:
+ """Update a rule result.
+
+ Only the fields you send are changed.
+
+ Use this to assign an owner, or to exclude a single result from compliance
+ without deactivating the rule everywhere. `deactivatedReason` is required when
+ setting `deactivated` to `true`.
+
+ A result's `status` is computed by Openlayer and cannot be set directly.
+
+ Args:
+ assignee_id: The user responsible for this result.
+
+ blocked_by: Rule results that must pass before this one can be satisfied.
+
+ blocking: Rule results that this one blocks.
+
+ deactivated: Whether this result is excluded from compliance calculations.
+
+ deactivated_reason: Why the result was excluded.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return self._patch(
+ path_template("/rule-results/{rule_result_id}", rule_result_id=rule_result_id),
+ body=maybe_transform(
+ {
+ "assignee_id": assignee_id,
+ "blocked_by": blocked_by,
+ "blocking": blocking,
+ "deactivated": deactivated,
+ "deactivated_reason": deactivated_reason,
+ },
+ rule_result_update_params.RuleResultUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleResultUpdateResponse,
+ )
+
+ def list(
+ self,
+ workspace_id: str,
+ *,
+ enabled_framework_only: bool | Omit = omit,
+ framework_id: str | Omit = omit,
+ include_unframed: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ rule_id: str | Omit = omit,
+ scope: Literal["project", "workspace"] | Omit = omit,
+ search_query: str | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ type: Literal["platform", "evidence"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultListResponse:
+ """
+ List rule results across a workspace.
+
+ A rule result is the compliance status of one rule for one entity: a project for
+ project-scoped rules, or the workspace itself for workspace-scoped rules. This
+ is the endpoint to poll or export when you want your current compliance state,
+ filtered to a framework, a project, or a status.
+
+ Args:
+ enabled_framework_only: Only include items belonging to at least one enabled framework.
+
+ framework_id: Only include items belonging to this framework.
+
+ include_unframed: Whether to include rules that are not part of any framework.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ rule_id: Only include results of this rule.
+
+ scope: Only include rules with this scope.
+
+ search_query: Filter by a free-text search over names and descriptions.
+
+ status: Only include items whose rule result has this compliance status.
+
+ type: Only include rules of this type.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._get(
+ path_template("/workspaces/{workspace_id}/rule-results", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "enabled_framework_only": enabled_framework_only,
+ "framework_id": framework_id,
+ "include_unframed": include_unframed,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "rule_id": rule_id,
+ "scope": scope,
+ "search_query": search_query,
+ "status": status,
+ "type": type,
+ },
+ rule_result_list_params.RuleResultListParams,
+ ),
+ ),
+ cast_to=RuleResultListResponse,
+ )
+
+ def create_evidence(
+ self,
+ rule_result_id: str,
+ *,
+ description: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ storage_uri: Optional[str] | Omit = omit,
+ text: Optional[str] | Omit = omit,
+ url: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultCreateEvidenceResponse:
+ """
+ Attach evidence to a rule result, satisfying an evidence rule.
+
+ Send the field that matches the rule's `evidenceType`: `storageUri` for an
+ uploaded document, `text` for a written statement, or `url` for a link.
+
+ For a document, upload the file first with `POST /storage/presigned-url` and
+ send the resulting storage URI as `storageUri`.
+
+ Attaching evidence re-evaluates the rule result. If the rule sets
+ `renewalCadenceDays`, the renewal window restarts from this evidence.
+
+ Args:
+ description: A description of what the evidence shows.
+
+ name: The evidence name.
+
+ storage_uri: Where the uploaded file is stored. Set when the rule's `evidenceType` is
+ `document`.
+
+ text: The evidence text. Set when the rule's `evidenceType` is `text`.
+
+ url: A link to the evidence. Set when the rule's `evidenceType` is `url`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return self._post(
+ path_template("/rule-results/{rule_result_id}/evidence", rule_result_id=rule_result_id),
+ body=maybe_transform(
+ {
+ "description": description,
+ "name": name,
+ "storage_uri": storage_uri,
+ "text": text,
+ "url": url,
+ },
+ rule_result_create_evidence_params.RuleResultCreateEvidenceParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleResultCreateEvidenceResponse,
+ )
+
+ def list_evidence(
+ self,
+ rule_result_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultListEvidenceResponse:
+ """
+ List the evidence attached to a rule result.
+
+ Which field carries the evidence depends on the rule's `evidenceType`:
+ `storageUri` for uploaded documents, `text` for written statements, and `url`
+ for links.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return self._get(
+ path_template("/rule-results/{rule_result_id}/evidence", rule_result_id=rule_result_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ rule_result_list_evidence_params.RuleResultListEvidenceParams,
+ ),
+ ),
+ cast_to=RuleResultListEvidenceResponse,
+ )
+
+
+class AsyncRuleResultsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncRuleResultsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncRuleResultsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncRuleResultsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncRuleResultsResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ rule_result_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultRetrieveResponse:
+ """
+ Retrieve a rule result by its id.
+
+ Alongside the status, the response carries the evaluation and renewal dates that
+ explain it: `dateLastEvaluated` and `dateOfNextEvaluation` for platform rules,
+ `dateOfLatestEvidence` and `dateOfRenewal` for evidence rules.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return await self._get(
+ path_template("/rule-results/{rule_result_id}", rule_result_id=rule_result_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleResultRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ rule_result_id: str,
+ *,
+ assignee_id: Optional[str] | Omit = omit,
+ blocked_by: Iterable[rule_result_update_params.BlockedBy] | Omit = omit,
+ blocking: Iterable[rule_result_update_params.Blocking] | Omit = omit,
+ deactivated: bool | Omit = omit,
+ deactivated_reason: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultUpdateResponse:
+ """Update a rule result.
+
+ Only the fields you send are changed.
+
+ Use this to assign an owner, or to exclude a single result from compliance
+ without deactivating the rule everywhere. `deactivatedReason` is required when
+ setting `deactivated` to `true`.
+
+ A result's `status` is computed by Openlayer and cannot be set directly.
+
+ Args:
+ assignee_id: The user responsible for this result.
+
+ blocked_by: Rule results that must pass before this one can be satisfied.
+
+ blocking: Rule results that this one blocks.
+
+ deactivated: Whether this result is excluded from compliance calculations.
+
+ deactivated_reason: Why the result was excluded.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return await self._patch(
+ path_template("/rule-results/{rule_result_id}", rule_result_id=rule_result_id),
+ body=await async_maybe_transform(
+ {
+ "assignee_id": assignee_id,
+ "blocked_by": blocked_by,
+ "blocking": blocking,
+ "deactivated": deactivated,
+ "deactivated_reason": deactivated_reason,
+ },
+ rule_result_update_params.RuleResultUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleResultUpdateResponse,
+ )
+
+ async def list(
+ self,
+ workspace_id: str,
+ *,
+ enabled_framework_only: bool | Omit = omit,
+ framework_id: str | Omit = omit,
+ include_unframed: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ rule_id: str | Omit = omit,
+ scope: Literal["project", "workspace"] | Omit = omit,
+ search_query: str | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ type: Literal["platform", "evidence"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultListResponse:
+ """
+ List rule results across a workspace.
+
+ A rule result is the compliance status of one rule for one entity: a project for
+ project-scoped rules, or the workspace itself for workspace-scoped rules. This
+ is the endpoint to poll or export when you want your current compliance state,
+ filtered to a framework, a project, or a status.
+
+ Args:
+ enabled_framework_only: Only include items belonging to at least one enabled framework.
+
+ framework_id: Only include items belonging to this framework.
+
+ include_unframed: Whether to include rules that are not part of any framework.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ rule_id: Only include results of this rule.
+
+ scope: Only include rules with this scope.
+
+ search_query: Filter by a free-text search over names and descriptions.
+
+ status: Only include items whose rule result has this compliance status.
+
+ type: Only include rules of this type.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._get(
+ path_template("/workspaces/{workspace_id}/rule-results", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "enabled_framework_only": enabled_framework_only,
+ "framework_id": framework_id,
+ "include_unframed": include_unframed,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "rule_id": rule_id,
+ "scope": scope,
+ "search_query": search_query,
+ "status": status,
+ "type": type,
+ },
+ rule_result_list_params.RuleResultListParams,
+ ),
+ ),
+ cast_to=RuleResultListResponse,
+ )
+
+ async def create_evidence(
+ self,
+ rule_result_id: str,
+ *,
+ description: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ storage_uri: Optional[str] | Omit = omit,
+ text: Optional[str] | Omit = omit,
+ url: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultCreateEvidenceResponse:
+ """
+ Attach evidence to a rule result, satisfying an evidence rule.
+
+ Send the field that matches the rule's `evidenceType`: `storageUri` for an
+ uploaded document, `text` for a written statement, or `url` for a link.
+
+ For a document, upload the file first with `POST /storage/presigned-url` and
+ send the resulting storage URI as `storageUri`.
+
+ Attaching evidence re-evaluates the rule result. If the rule sets
+ `renewalCadenceDays`, the renewal window restarts from this evidence.
+
+ Args:
+ description: A description of what the evidence shows.
+
+ name: The evidence name.
+
+ storage_uri: Where the uploaded file is stored. Set when the rule's `evidenceType` is
+ `document`.
+
+ text: The evidence text. Set when the rule's `evidenceType` is `text`.
+
+ url: A link to the evidence. Set when the rule's `evidenceType` is `url`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return await self._post(
+ path_template("/rule-results/{rule_result_id}/evidence", rule_result_id=rule_result_id),
+ body=await async_maybe_transform(
+ {
+ "description": description,
+ "name": name,
+ "storage_uri": storage_uri,
+ "text": text,
+ "url": url,
+ },
+ rule_result_create_evidence_params.RuleResultCreateEvidenceParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleResultCreateEvidenceResponse,
+ )
+
+ async def list_evidence(
+ self,
+ rule_result_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleResultListEvidenceResponse:
+ """
+ List the evidence attached to a rule result.
+
+ Which field carries the evidence depends on the rule's `evidenceType`:
+ `storageUri` for uploaded documents, `text` for written statements, and `url`
+ for links.
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_result_id:
+ raise ValueError(f"Expected a non-empty value for `rule_result_id` but received {rule_result_id!r}")
+ return await self._get(
+ path_template("/rule-results/{rule_result_id}/evidence", rule_result_id=rule_result_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ rule_result_list_evidence_params.RuleResultListEvidenceParams,
+ ),
+ ),
+ cast_to=RuleResultListEvidenceResponse,
+ )
+
+
+class RuleResultsResourceWithRawResponse:
+ def __init__(self, rule_results: RuleResultsResource) -> None:
+ self._rule_results = rule_results
+
+ self.retrieve = to_raw_response_wrapper(
+ rule_results.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ rule_results.update,
+ )
+ self.list = to_raw_response_wrapper(
+ rule_results.list,
+ )
+ self.create_evidence = to_raw_response_wrapper(
+ rule_results.create_evidence,
+ )
+ self.list_evidence = to_raw_response_wrapper(
+ rule_results.list_evidence,
+ )
+
+
+class AsyncRuleResultsResourceWithRawResponse:
+ def __init__(self, rule_results: AsyncRuleResultsResource) -> None:
+ self._rule_results = rule_results
+
+ self.retrieve = async_to_raw_response_wrapper(
+ rule_results.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ rule_results.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ rule_results.list,
+ )
+ self.create_evidence = async_to_raw_response_wrapper(
+ rule_results.create_evidence,
+ )
+ self.list_evidence = async_to_raw_response_wrapper(
+ rule_results.list_evidence,
+ )
+
+
+class RuleResultsResourceWithStreamingResponse:
+ def __init__(self, rule_results: RuleResultsResource) -> None:
+ self._rule_results = rule_results
+
+ self.retrieve = to_streamed_response_wrapper(
+ rule_results.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ rule_results.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ rule_results.list,
+ )
+ self.create_evidence = to_streamed_response_wrapper(
+ rule_results.create_evidence,
+ )
+ self.list_evidence = to_streamed_response_wrapper(
+ rule_results.list_evidence,
+ )
+
+
+class AsyncRuleResultsResourceWithStreamingResponse:
+ def __init__(self, rule_results: AsyncRuleResultsResource) -> None:
+ self._rule_results = rule_results
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ rule_results.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ rule_results.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ rule_results.list,
+ )
+ self.create_evidence = async_to_streamed_response_wrapper(
+ rule_results.create_evidence,
+ )
+ self.list_evidence = async_to_streamed_response_wrapper(
+ rule_results.list_evidence,
+ )
diff --git a/src/openlayer/resources/governance/rule_stats.py b/src/openlayer/resources/governance/rule_stats.py
new file mode 100644
index 00000000..52edc7d5
--- /dev/null
+++ b/src/openlayer/resources/governance/rule_stats.py
@@ -0,0 +1,205 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.governance import rule_stat_retrieve_params
+from ...types.governance.rule_stat_retrieve_response import RuleStatRetrieveResponse
+
+__all__ = ["RuleStatsResource", "AsyncRuleStatsResource"]
+
+
+class RuleStatsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> RuleStatsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return RuleStatsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> RuleStatsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return RuleStatsResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ workspace_id: str,
+ *,
+ framework_id: str | Omit = omit,
+ project_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleStatRetrieveResponse:
+ """
+ Get a compliance roll-up for a workspace: how many rules exist, and how many of
+ their results are passing, failing, pending, or due for renewal.
+
+ Counts respect the filters you pass, so `frameworkId` gives you a single
+ framework's overall compliance and `projectId` gives you a single project's.
+
+ Args:
+ framework_id: Only include items belonging to this framework.
+
+ project_id: Only include items that apply to this project.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._get(
+ path_template("/workspaces/{workspace_id}/rule-stats", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "framework_id": framework_id,
+ "project_id": project_id,
+ },
+ rule_stat_retrieve_params.RuleStatRetrieveParams,
+ ),
+ ),
+ cast_to=RuleStatRetrieveResponse,
+ )
+
+
+class AsyncRuleStatsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncRuleStatsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncRuleStatsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncRuleStatsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncRuleStatsResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ workspace_id: str,
+ *,
+ framework_id: str | Omit = omit,
+ project_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleStatRetrieveResponse:
+ """
+ Get a compliance roll-up for a workspace: how many rules exist, and how many of
+ their results are passing, failing, pending, or due for renewal.
+
+ Counts respect the filters you pass, so `frameworkId` gives you a single
+ framework's overall compliance and `projectId` gives you a single project's.
+
+ Args:
+ framework_id: Only include items belonging to this framework.
+
+ project_id: Only include items that apply to this project.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._get(
+ path_template("/workspaces/{workspace_id}/rule-stats", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "framework_id": framework_id,
+ "project_id": project_id,
+ },
+ rule_stat_retrieve_params.RuleStatRetrieveParams,
+ ),
+ ),
+ cast_to=RuleStatRetrieveResponse,
+ )
+
+
+class RuleStatsResourceWithRawResponse:
+ def __init__(self, rule_stats: RuleStatsResource) -> None:
+ self._rule_stats = rule_stats
+
+ self.retrieve = to_raw_response_wrapper(
+ rule_stats.retrieve,
+ )
+
+
+class AsyncRuleStatsResourceWithRawResponse:
+ def __init__(self, rule_stats: AsyncRuleStatsResource) -> None:
+ self._rule_stats = rule_stats
+
+ self.retrieve = async_to_raw_response_wrapper(
+ rule_stats.retrieve,
+ )
+
+
+class RuleStatsResourceWithStreamingResponse:
+ def __init__(self, rule_stats: RuleStatsResource) -> None:
+ self._rule_stats = rule_stats
+
+ self.retrieve = to_streamed_response_wrapper(
+ rule_stats.retrieve,
+ )
+
+
+class AsyncRuleStatsResourceWithStreamingResponse:
+ def __init__(self, rule_stats: AsyncRuleStatsResource) -> None:
+ self._rule_stats = rule_stats
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ rule_stats.retrieve,
+ )
diff --git a/src/openlayer/resources/governance/rule_tags.py b/src/openlayer/resources/governance/rule_tags.py
new file mode 100644
index 00000000..aea39688
--- /dev/null
+++ b/src/openlayer/resources/governance/rule_tags.py
@@ -0,0 +1,205 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.governance import rule_tag_list_params
+from ...types.governance.rule_tag_list_response import RuleTagListResponse
+
+__all__ = ["RuleTagsResource", "AsyncRuleTagsResource"]
+
+
+class RuleTagsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> RuleTagsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return RuleTagsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> RuleTagsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return RuleTagsResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ workspace_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleTagListResponse:
+ """
+ List the rule tags in a workspace.
+
+ Tags group rules across frameworks, for example by team or by control family.
+ Use the ids returned here with the `tags` filter on
+ [List rules](/api-reference/rest/governance/list-rules).
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._get(
+ path_template("/workspaces/{workspace_id}/rule-tags", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ rule_tag_list_params.RuleTagListParams,
+ ),
+ ),
+ cast_to=RuleTagListResponse,
+ )
+
+
+class AsyncRuleTagsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncRuleTagsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncRuleTagsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncRuleTagsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncRuleTagsResourceWithStreamingResponse(self)
+
+ async def list(
+ self,
+ workspace_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleTagListResponse:
+ """
+ List the rule tags in a workspace.
+
+ Tags group rules across frameworks, for example by team or by control family.
+ Use the ids returned here with the `tags` filter on
+ [List rules](/api-reference/rest/governance/list-rules).
+
+ Args:
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._get(
+ path_template("/workspaces/{workspace_id}/rule-tags", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ rule_tag_list_params.RuleTagListParams,
+ ),
+ ),
+ cast_to=RuleTagListResponse,
+ )
+
+
+class RuleTagsResourceWithRawResponse:
+ def __init__(self, rule_tags: RuleTagsResource) -> None:
+ self._rule_tags = rule_tags
+
+ self.list = to_raw_response_wrapper(
+ rule_tags.list,
+ )
+
+
+class AsyncRuleTagsResourceWithRawResponse:
+ def __init__(self, rule_tags: AsyncRuleTagsResource) -> None:
+ self._rule_tags = rule_tags
+
+ self.list = async_to_raw_response_wrapper(
+ rule_tags.list,
+ )
+
+
+class RuleTagsResourceWithStreamingResponse:
+ def __init__(self, rule_tags: RuleTagsResource) -> None:
+ self._rule_tags = rule_tags
+
+ self.list = to_streamed_response_wrapper(
+ rule_tags.list,
+ )
+
+
+class AsyncRuleTagsResourceWithStreamingResponse:
+ def __init__(self, rule_tags: AsyncRuleTagsResource) -> None:
+ self._rule_tags = rule_tags
+
+ self.list = async_to_streamed_response_wrapper(
+ rule_tags.list,
+ )
diff --git a/src/openlayer/resources/governance/rules.py b/src/openlayer/resources/governance/rules.py
new file mode 100644
index 00000000..04251098
--- /dev/null
+++ b/src/openlayer/resources/governance/rules.py
@@ -0,0 +1,885 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.governance import rule_list_params, rule_create_params, rule_update_params
+from ...types.governance.rule_list_response import RuleListResponse
+from ...types.governance.rule_create_response import RuleCreateResponse
+from ...types.governance.rule_update_response import RuleUpdateResponse
+from ...types.governance.rule_retrieve_response import RuleRetrieveResponse
+
+__all__ = ["RulesResource", "AsyncRulesResource"]
+
+
+class RulesResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> RulesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return RulesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> RulesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return RulesResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ workspace_id: str,
+ *,
+ name: str,
+ scope: Literal["project", "workspace"],
+ type: Literal["platform", "evidence"],
+ assignee_id: Optional[str] | Omit = omit,
+ automation_params: Optional[Dict[str, object]] | Omit = omit,
+ automation_type: Optional[str] | Omit = omit,
+ deactivated: bool | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] | Omit = omit,
+ renewal_cadence_days: Optional[int] | Omit = omit,
+ tag_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleCreateResponse:
+ """Create a governance rule in a workspace.
+
+ A rule is one requirement.
+
+ Its `type` decides how it is satisfied, and the two
+ types accept different fields:
+
+ - `platform` rules are evaluated automatically from the state of your workspace.
+ Set `automationType` to the signal to check. Their `scope` must be `project`,
+ and `evidenceType` and `renewalCadenceDays` must be omitted or `null`.
+ - `evidence` rules are satisfied by attaching evidence. Set `evidenceType` to
+ the kind of evidence that satisfies them. `automationType` and
+ `automationParams` must be omitted or `null`.
+
+ A new rule belongs to no framework. Map it to one from the Openlayer app.
+
+ Args:
+ name: The rule name.
+
+ scope: Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+
+ type: `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+
+ assignee_id: The user responsible for satisfying the rule.
+
+ automation_params: Configuration for the platform check, when the automation takes parameters.
+
+ automation_type: Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+
+ deactivated: Whether the rule is excluded from compliance calculations.
+
+ description: What the rule requires.
+
+ evidence_type: The kind of evidence that satisfies the rule. `null` for platform rules.
+
+ renewal_cadence_days: How often evidence must be renewed, in days. Once evidence is older than this,
+ the rule result becomes `due_soon` and then `failing`.
+
+ tag_ids: The ids of the rule tags to associate with the rule. Replaces the rule's tags.
+ Read them back from `tags`, and list the tags available in the workspace with
+ `GET /workspaces/{workspaceId}/rule-tags`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._post(
+ path_template("/workspaces/{workspace_id}/rules", workspace_id=workspace_id),
+ body=maybe_transform(
+ {
+ "name": name,
+ "scope": scope,
+ "type": type,
+ "assignee_id": assignee_id,
+ "automation_params": automation_params,
+ "automation_type": automation_type,
+ "deactivated": deactivated,
+ "description": description,
+ "evidence_type": evidence_type,
+ "renewal_cadence_days": renewal_cadence_days,
+ "tag_ids": tag_ids,
+ },
+ rule_create_params.RuleCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ rule_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleRetrieveResponse:
+ """
+ Retrieve a governance rule by its id, including the frameworks it belongs to and
+ its tags.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_id:
+ raise ValueError(f"Expected a non-empty value for `rule_id` but received {rule_id!r}")
+ return self._get(
+ path_template("/rules/{rule_id}", rule_id=rule_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleRetrieveResponse,
+ )
+
+ def update(
+ self,
+ rule_id: str,
+ *,
+ assignee_id: Optional[str] | Omit = omit,
+ deactivated: bool | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ name: str | Omit = omit,
+ renewal_cadence_days: Optional[int] | Omit = omit,
+ tag_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleUpdateResponse:
+ """Update a governance rule.
+
+ Only the fields you send are changed.
+
+ Rules that ship with Openlayer report `immutable: true` and cannot be edited.
+
+ A rule's `scope`, `type`, `evidenceType`, and automation are fixed once it
+ exists -- create a new rule instead of converting one.
+
+ Args:
+ assignee_id: The user responsible for satisfying the rule.
+
+ deactivated: Whether the rule is excluded from compliance calculations.
+
+ description: What the rule requires.
+
+ name: The rule name.
+
+ renewal_cadence_days: How often evidence must be renewed, in days. Once evidence is older than this,
+ the rule result becomes `due_soon` and then `failing`.
+
+ tag_ids: The ids of the rule tags to associate with the rule. Replaces the rule's tags.
+ Read them back from `tags`, and list the tags available in the workspace with
+ `GET /workspaces/{workspaceId}/rule-tags`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_id:
+ raise ValueError(f"Expected a non-empty value for `rule_id` but received {rule_id!r}")
+ return self._put(
+ path_template("/rules/{rule_id}", rule_id=rule_id),
+ body=maybe_transform(
+ {
+ "assignee_id": assignee_id,
+ "deactivated": deactivated,
+ "description": description,
+ "name": name,
+ "renewal_cadence_days": renewal_cadence_days,
+ "tag_ids": tag_ids,
+ },
+ rule_update_params.RuleUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleUpdateResponse,
+ )
+
+ def list(
+ self,
+ workspace_id: str,
+ *,
+ asc: bool | Omit = omit,
+ assignee_id: str | Omit = omit,
+ deactivated: bool | Omit = omit,
+ enabled_framework_only: bool | Omit = omit,
+ framework_id: str | Omit = omit,
+ group: Literal["open", "excluded", "done"] | Omit = omit,
+ include_results: bool | Omit = omit,
+ include_unframed: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ scope: Literal["project", "workspace"] | Omit = omit,
+ search_query: str | Omit = omit,
+ sort_by: Literal["name", "status", "frameworks", "scope", "dateCreated"] | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ type: Literal["platform", "evidence"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleListResponse:
+ """
+ List the governance rules in a workspace.
+
+ A rule is a single requirement Openlayer tracks. `platform` rules are evaluated
+ automatically from the state of your workspace; `evidence` rules are satisfied
+ by attaching evidence. A rule can belong to several frameworks at once, and
+ rules that belong to none are returned too unless you pass
+ `includeUnframed=false`.
+
+ Pass `includeResults=true` to get each rule's compliance results inline instead
+ of fetching them separately.
+
+ Args:
+ asc: Whether to sort in ascending order.
+
+ assignee_id: Only include rules assigned to this user.
+
+ deactivated: Only include rules that are deactivated (or active).
+
+ enabled_framework_only: Only include items belonging to at least one enabled framework.
+
+ framework_id: Only include items belonging to this framework.
+
+ group: Only include rules in one bucket of the compliance workflow. `open` covers rules
+ that still need attention, `done` covers rules that are fully satisfied, and
+ `excluded` covers rules that have been deactivated.
+
+ include_results: Whether to include each rule's results inline, in a `results` array.
+
+ include_unframed: Whether to include rules that are not part of any framework.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ scope: Only include rules with this scope.
+
+ search_query: Filter by a free-text search over names and descriptions.
+
+ sort_by: The field to sort on.
+
+ status: Only include items whose rule result has this compliance status.
+
+ tags: Only include rules carrying all of these rule tags. Pass tag ids, which you can
+ look up with [List rule tags](/api-reference/rest/governance/list-rule-tags).
+
+ type: Only include rules of this type.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return self._get(
+ path_template("/workspaces/{workspace_id}/rules", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "asc": asc,
+ "assignee_id": assignee_id,
+ "deactivated": deactivated,
+ "enabled_framework_only": enabled_framework_only,
+ "framework_id": framework_id,
+ "group": group,
+ "include_results": include_results,
+ "include_unframed": include_unframed,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "scope": scope,
+ "search_query": search_query,
+ "sort_by": sort_by,
+ "status": status,
+ "tags": tags,
+ "type": type,
+ },
+ rule_list_params.RuleListParams,
+ ),
+ ),
+ cast_to=RuleListResponse,
+ )
+
+ def delete(
+ self,
+ rule_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Delete a governance rule and its rule results.
+
+ Only rules you created can be deleted. Rules that ship with Openlayer report
+ `immutable: true` and cannot be deleted -- exclude one from compliance by
+ setting `deactivated` with `PUT /rules/{ruleId}` instead.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_id:
+ raise ValueError(f"Expected a non-empty value for `rule_id` but received {rule_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/rules/{rule_id}", rule_id=rule_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncRulesResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncRulesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncRulesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncRulesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/openlayer-ai/openlayer-python#with_streaming_response
+ """
+ return AsyncRulesResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ workspace_id: str,
+ *,
+ name: str,
+ scope: Literal["project", "workspace"],
+ type: Literal["platform", "evidence"],
+ assignee_id: Optional[str] | Omit = omit,
+ automation_params: Optional[Dict[str, object]] | Omit = omit,
+ automation_type: Optional[str] | Omit = omit,
+ deactivated: bool | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] | Omit = omit,
+ renewal_cadence_days: Optional[int] | Omit = omit,
+ tag_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleCreateResponse:
+ """Create a governance rule in a workspace.
+
+ A rule is one requirement.
+
+ Its `type` decides how it is satisfied, and the two
+ types accept different fields:
+
+ - `platform` rules are evaluated automatically from the state of your workspace.
+ Set `automationType` to the signal to check. Their `scope` must be `project`,
+ and `evidenceType` and `renewalCadenceDays` must be omitted or `null`.
+ - `evidence` rules are satisfied by attaching evidence. Set `evidenceType` to
+ the kind of evidence that satisfies them. `automationType` and
+ `automationParams` must be omitted or `null`.
+
+ A new rule belongs to no framework. Map it to one from the Openlayer app.
+
+ Args:
+ name: The rule name.
+
+ scope: Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+
+ type: `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+
+ assignee_id: The user responsible for satisfying the rule.
+
+ automation_params: Configuration for the platform check, when the automation takes parameters.
+
+ automation_type: Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+
+ deactivated: Whether the rule is excluded from compliance calculations.
+
+ description: What the rule requires.
+
+ evidence_type: The kind of evidence that satisfies the rule. `null` for platform rules.
+
+ renewal_cadence_days: How often evidence must be renewed, in days. Once evidence is older than this,
+ the rule result becomes `due_soon` and then `failing`.
+
+ tag_ids: The ids of the rule tags to associate with the rule. Replaces the rule's tags.
+ Read them back from `tags`, and list the tags available in the workspace with
+ `GET /workspaces/{workspaceId}/rule-tags`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._post(
+ path_template("/workspaces/{workspace_id}/rules", workspace_id=workspace_id),
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "scope": scope,
+ "type": type,
+ "assignee_id": assignee_id,
+ "automation_params": automation_params,
+ "automation_type": automation_type,
+ "deactivated": deactivated,
+ "description": description,
+ "evidence_type": evidence_type,
+ "renewal_cadence_days": renewal_cadence_days,
+ "tag_ids": tag_ids,
+ },
+ rule_create_params.RuleCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ rule_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleRetrieveResponse:
+ """
+ Retrieve a governance rule by its id, including the frameworks it belongs to and
+ its tags.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_id:
+ raise ValueError(f"Expected a non-empty value for `rule_id` but received {rule_id!r}")
+ return await self._get(
+ path_template("/rules/{rule_id}", rule_id=rule_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ rule_id: str,
+ *,
+ assignee_id: Optional[str] | Omit = omit,
+ deactivated: bool | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ name: str | Omit = omit,
+ renewal_cadence_days: Optional[int] | Omit = omit,
+ tag_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleUpdateResponse:
+ """Update a governance rule.
+
+ Only the fields you send are changed.
+
+ Rules that ship with Openlayer report `immutable: true` and cannot be edited.
+
+ A rule's `scope`, `type`, `evidenceType`, and automation are fixed once it
+ exists -- create a new rule instead of converting one.
+
+ Args:
+ assignee_id: The user responsible for satisfying the rule.
+
+ deactivated: Whether the rule is excluded from compliance calculations.
+
+ description: What the rule requires.
+
+ name: The rule name.
+
+ renewal_cadence_days: How often evidence must be renewed, in days. Once evidence is older than this,
+ the rule result becomes `due_soon` and then `failing`.
+
+ tag_ids: The ids of the rule tags to associate with the rule. Replaces the rule's tags.
+ Read them back from `tags`, and list the tags available in the workspace with
+ `GET /workspaces/{workspaceId}/rule-tags`.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_id:
+ raise ValueError(f"Expected a non-empty value for `rule_id` but received {rule_id!r}")
+ return await self._put(
+ path_template("/rules/{rule_id}", rule_id=rule_id),
+ body=await async_maybe_transform(
+ {
+ "assignee_id": assignee_id,
+ "deactivated": deactivated,
+ "description": description,
+ "name": name,
+ "renewal_cadence_days": renewal_cadence_days,
+ "tag_ids": tag_ids,
+ },
+ rule_update_params.RuleUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RuleUpdateResponse,
+ )
+
+ async def list(
+ self,
+ workspace_id: str,
+ *,
+ asc: bool | Omit = omit,
+ assignee_id: str | Omit = omit,
+ deactivated: bool | Omit = omit,
+ enabled_framework_only: bool | Omit = omit,
+ framework_id: str | Omit = omit,
+ group: Literal["open", "excluded", "done"] | Omit = omit,
+ include_results: bool | Omit = omit,
+ include_unframed: bool | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ project_id: str | Omit = omit,
+ scope: Literal["project", "workspace"] | Omit = omit,
+ search_query: str | Omit = omit,
+ sort_by: Literal["name", "status", "frameworks", "scope", "dateCreated"] | Omit = omit,
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"] | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ type: Literal["platform", "evidence"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RuleListResponse:
+ """
+ List the governance rules in a workspace.
+
+ A rule is a single requirement Openlayer tracks. `platform` rules are evaluated
+ automatically from the state of your workspace; `evidence` rules are satisfied
+ by attaching evidence. A rule can belong to several frameworks at once, and
+ rules that belong to none are returned too unless you pass
+ `includeUnframed=false`.
+
+ Pass `includeResults=true` to get each rule's compliance results inline instead
+ of fetching them separately.
+
+ Args:
+ asc: Whether to sort in ascending order.
+
+ assignee_id: Only include rules assigned to this user.
+
+ deactivated: Only include rules that are deactivated (or active).
+
+ enabled_framework_only: Only include items belonging to at least one enabled framework.
+
+ framework_id: Only include items belonging to this framework.
+
+ group: Only include rules in one bucket of the compliance workflow. `open` covers rules
+ that still need attention, `done` covers rules that are fully satisfied, and
+ `excluded` covers rules that have been deactivated.
+
+ include_results: Whether to include each rule's results inline, in a `results` array.
+
+ include_unframed: Whether to include rules that are not part of any framework.
+
+ page: The page to return in a paginated query.
+
+ per_page: Maximum number of items to return per page.
+
+ project_id: Only include items that apply to this project.
+
+ scope: Only include rules with this scope.
+
+ search_query: Filter by a free-text search over names and descriptions.
+
+ sort_by: The field to sort on.
+
+ status: Only include items whose rule result has this compliance status.
+
+ tags: Only include rules carrying all of these rule tags. Pass tag ids, which you can
+ look up with [List rule tags](/api-reference/rest/governance/list-rule-tags).
+
+ type: Only include rules of this type.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not workspace_id:
+ raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}")
+ return await self._get(
+ path_template("/workspaces/{workspace_id}/rules", workspace_id=workspace_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "asc": asc,
+ "assignee_id": assignee_id,
+ "deactivated": deactivated,
+ "enabled_framework_only": enabled_framework_only,
+ "framework_id": framework_id,
+ "group": group,
+ "include_results": include_results,
+ "include_unframed": include_unframed,
+ "page": page,
+ "per_page": per_page,
+ "project_id": project_id,
+ "scope": scope,
+ "search_query": search_query,
+ "sort_by": sort_by,
+ "status": status,
+ "tags": tags,
+ "type": type,
+ },
+ rule_list_params.RuleListParams,
+ ),
+ ),
+ cast_to=RuleListResponse,
+ )
+
+ async def delete(
+ self,
+ rule_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Delete a governance rule and its rule results.
+
+ Only rules you created can be deleted. Rules that ship with Openlayer report
+ `immutable: true` and cannot be deleted -- exclude one from compliance by
+ setting `deactivated` with `PUT /rules/{ruleId}` instead.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not rule_id:
+ raise ValueError(f"Expected a non-empty value for `rule_id` but received {rule_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/rules/{rule_id}", rule_id=rule_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class RulesResourceWithRawResponse:
+ def __init__(self, rules: RulesResource) -> None:
+ self._rules = rules
+
+ self.create = to_raw_response_wrapper(
+ rules.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ rules.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ rules.update,
+ )
+ self.list = to_raw_response_wrapper(
+ rules.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ rules.delete,
+ )
+
+
+class AsyncRulesResourceWithRawResponse:
+ def __init__(self, rules: AsyncRulesResource) -> None:
+ self._rules = rules
+
+ self.create = async_to_raw_response_wrapper(
+ rules.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ rules.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ rules.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ rules.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ rules.delete,
+ )
+
+
+class RulesResourceWithStreamingResponse:
+ def __init__(self, rules: RulesResource) -> None:
+ self._rules = rules
+
+ self.create = to_streamed_response_wrapper(
+ rules.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ rules.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ rules.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ rules.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ rules.delete,
+ )
+
+
+class AsyncRulesResourceWithStreamingResponse:
+ def __init__(self, rules: AsyncRulesResource) -> None:
+ self._rules = rules
+
+ self.create = async_to_streamed_response_wrapper(
+ rules.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ rules.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ rules.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ rules.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ rules.delete,
+ )
diff --git a/src/openlayer/resources/storage/presigned_url.py b/src/openlayer/resources/storage/presigned_url.py
index 96886fe8..42a7a1de 100644
--- a/src/openlayer/resources/storage/presigned_url.py
+++ b/src/openlayer/resources/storage/presigned_url.py
@@ -15,8 +15,9 @@
async_to_streamed_response_wrapper,
)
from ..._base_client import make_request_options
-from ...types.storage import presigned_url_create_params
+from ...types.storage import presigned_url_create_params, presigned_url_retrieve_params
from ...types.storage.presigned_url_create_response import PresignedURLCreateResponse
+from ...types.storage.presigned_url_retrieve_response import PresignedURLRetrieveResponse
__all__ = ["PresignedURLResource", "AsyncPresignedURLResource"]
@@ -80,6 +81,54 @@ def create(
cast_to=PresignedURLCreateResponse,
)
+ def retrieve(
+ self,
+ *,
+ storage_uri: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> PresignedURLRetrieveResponse:
+ """
+ Exchange a `storageUri` for a short-lived presigned url you can download the
+ object from.
+
+ Use it to collect anything the platform stored on your behalf -- for example the
+ archive a framework export leaves behind, whose `storageUri` comes back in the
+ background task's `outputs`.
+
+ The workspace is taken from the API key, so there is nothing else to send. The
+ url is only issued for objects your workspace owns, and `404` covers both "no
+ such object" and "not yours".
+
+ Args:
+ storage_uri: The object's storage uri.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/storage/presigned-url",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {"storage_uri": storage_uri}, presigned_url_retrieve_params.PresignedURLRetrieveParams
+ ),
+ ),
+ cast_to=PresignedURLRetrieveResponse,
+ )
+
class AsyncPresignedURLResource(AsyncAPIResource):
@cached_property
@@ -140,6 +189,54 @@ async def create(
cast_to=PresignedURLCreateResponse,
)
+ async def retrieve(
+ self,
+ *,
+ storage_uri: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> PresignedURLRetrieveResponse:
+ """
+ Exchange a `storageUri` for a short-lived presigned url you can download the
+ object from.
+
+ Use it to collect anything the platform stored on your behalf -- for example the
+ archive a framework export leaves behind, whose `storageUri` comes back in the
+ background task's `outputs`.
+
+ The workspace is taken from the API key, so there is nothing else to send. The
+ url is only issued for objects your workspace owns, and `404` covers both "no
+ such object" and "not yours".
+
+ Args:
+ storage_uri: The object's storage uri.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/storage/presigned-url",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"storage_uri": storage_uri}, presigned_url_retrieve_params.PresignedURLRetrieveParams
+ ),
+ ),
+ cast_to=PresignedURLRetrieveResponse,
+ )
+
class PresignedURLResourceWithRawResponse:
def __init__(self, presigned_url: PresignedURLResource) -> None:
@@ -148,6 +245,9 @@ def __init__(self, presigned_url: PresignedURLResource) -> None:
self.create = to_raw_response_wrapper(
presigned_url.create,
)
+ self.retrieve = to_raw_response_wrapper(
+ presigned_url.retrieve,
+ )
class AsyncPresignedURLResourceWithRawResponse:
@@ -157,6 +257,9 @@ def __init__(self, presigned_url: AsyncPresignedURLResource) -> None:
self.create = async_to_raw_response_wrapper(
presigned_url.create,
)
+ self.retrieve = async_to_raw_response_wrapper(
+ presigned_url.retrieve,
+ )
class PresignedURLResourceWithStreamingResponse:
@@ -166,6 +269,9 @@ def __init__(self, presigned_url: PresignedURLResource) -> None:
self.create = to_streamed_response_wrapper(
presigned_url.create,
)
+ self.retrieve = to_streamed_response_wrapper(
+ presigned_url.retrieve,
+ )
class AsyncPresignedURLResourceWithStreamingResponse:
@@ -175,3 +281,6 @@ def __init__(self, presigned_url: AsyncPresignedURLResource) -> None:
self.create = async_to_streamed_response_wrapper(
presigned_url.create,
)
+ self.retrieve = async_to_streamed_response_wrapper(
+ presigned_url.retrieve,
+ )
diff --git a/src/openlayer/types/__init__.py b/src/openlayer/types/__init__.py
index b5752698..09a173ed 100644
--- a/src/openlayer/types/__init__.py
+++ b/src/openlayer/types/__init__.py
@@ -17,6 +17,7 @@
from .test_list_results_response import TestListResultsResponse as TestListResultsResponse
from .workspace_retrieve_response import WorkspaceRetrieveResponse as WorkspaceRetrieveResponse
from .inference_pipeline_update_params import InferencePipelineUpdateParams as InferencePipelineUpdateParams
+from .background_task_retrieve_response import BackgroundTaskRetrieveResponse as BackgroundTaskRetrieveResponse
from .inference_pipeline_retrieve_params import InferencePipelineRetrieveParams as InferencePipelineRetrieveParams
from .inference_pipeline_update_response import InferencePipelineUpdateResponse as InferencePipelineUpdateResponse
from .inference_pipeline_retrieve_response import InferencePipelineRetrieveResponse as InferencePipelineRetrieveResponse
diff --git a/src/openlayer/types/background_task_retrieve_response.py b/src/openlayer/types/background_task_retrieve_response.py
new file mode 100644
index 00000000..fb2ce9ac
--- /dev/null
+++ b/src/openlayer/types/background_task_retrieve_response.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["BackgroundTaskRetrieveResponse"]
+
+
+class BackgroundTaskRetrieveResponse(BaseModel):
+ id: str
+ """The background task id."""
+
+ complete: bool
+ """Whether the task has finished. Check this before reading `outputs`."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """When the task was queued."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """When the task last reported progress."""
+
+ name: str
+ """The task's internal name, including the arguments it was queued with."""
+
+ progress: float
+ """How far along the task is, from 0 to 100."""
+
+ error: Optional[str] = None
+ """Why the task failed, or `null` if it has not failed."""
+
+ outputs: Optional[object] = None
+ """Whatever the task produced, keyed by name.
+
+ `null` until the task completes. A framework export returns `storageUri` -- pass
+ it to `GET /storage/presigned-url` to download the archive -- along with
+ `filename`, `controlCount`, `evidenceCount` and `missingEvidenceCount`.
+ """
diff --git a/src/openlayer/types/governance/__init__.py b/src/openlayer/types/governance/__init__.py
new file mode 100644
index 00000000..cf3af05a
--- /dev/null
+++ b/src/openlayer/types/governance/__init__.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .rule_list_params import RuleListParams as RuleListParams
+from .rule_create_params import RuleCreateParams as RuleCreateParams
+from .rule_list_response import RuleListResponse as RuleListResponse
+from .rule_update_params import RuleUpdateParams as RuleUpdateParams
+from .rule_create_response import RuleCreateResponse as RuleCreateResponse
+from .rule_tag_list_params import RuleTagListParams as RuleTagListParams
+from .rule_update_response import RuleUpdateResponse as RuleUpdateResponse
+from .framework_list_params import FrameworkListParams as FrameworkListParams
+from .rule_retrieve_response import RuleRetrieveResponse as RuleRetrieveResponse
+from .rule_tag_list_response import RuleTagListResponse as RuleTagListResponse
+from .framework_create_params import FrameworkCreateParams as FrameworkCreateParams
+from .framework_export_params import FrameworkExportParams as FrameworkExportParams
+from .framework_list_response import FrameworkListResponse as FrameworkListResponse
+from .framework_update_params import FrameworkUpdateParams as FrameworkUpdateParams
+from .rule_result_list_params import RuleResultListParams as RuleResultListParams
+from .framework_create_response import FrameworkCreateResponse as FrameworkCreateResponse
+from .framework_export_response import FrameworkExportResponse as FrameworkExportResponse
+from .framework_update_response import FrameworkUpdateResponse as FrameworkUpdateResponse
+from .rule_result_list_response import RuleResultListResponse as RuleResultListResponse
+from .rule_result_update_params import RuleResultUpdateParams as RuleResultUpdateParams
+from .rule_stat_retrieve_params import RuleStatRetrieveParams as RuleStatRetrieveParams
+from .framework_list_rules_params import FrameworkListRulesParams as FrameworkListRulesParams
+from .framework_retrieve_response import FrameworkRetrieveResponse as FrameworkRetrieveResponse
+from .rule_result_update_response import RuleResultUpdateResponse as RuleResultUpdateResponse
+from .rule_stat_retrieve_response import RuleStatRetrieveResponse as RuleStatRetrieveResponse
+from .framework_list_rules_response import FrameworkListRulesResponse as FrameworkListRulesResponse
+from .rule_result_retrieve_response import RuleResultRetrieveResponse as RuleResultRetrieveResponse
+from .framework_list_projects_params import FrameworkListProjectsParams as FrameworkListProjectsParams
+from .framework_list_projects_response import FrameworkListProjectsResponse as FrameworkListProjectsResponse
+from .rule_result_list_evidence_params import RuleResultListEvidenceParams as RuleResultListEvidenceParams
+from .rule_result_create_evidence_params import RuleResultCreateEvidenceParams as RuleResultCreateEvidenceParams
+from .rule_result_list_evidence_response import RuleResultListEvidenceResponse as RuleResultListEvidenceResponse
+from .rule_result_create_evidence_response import RuleResultCreateEvidenceResponse as RuleResultCreateEvidenceResponse
+from .framework_list_project_rule_stats_params import (
+ FrameworkListProjectRuleStatsParams as FrameworkListProjectRuleStatsParams,
+)
+from .framework_list_project_rule_stats_response import (
+ FrameworkListProjectRuleStatsResponse as FrameworkListProjectRuleStatsResponse,
+)
diff --git a/src/openlayer/types/governance/framework_create_params.py b/src/openlayer/types/governance/framework_create_params.py
new file mode 100644
index 00000000..9af36302
--- /dev/null
+++ b/src/openlayer/types/governance/framework_create_params.py
@@ -0,0 +1,67 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkCreateParams", "ProjectSelector", "ProjectSelectorMatch"]
+
+
+class FrameworkCreateParams(TypedDict, total=False):
+ name: Required[str]
+ """The framework name."""
+
+ description: Optional[str]
+ """A short description of the framework."""
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ project_selector: Annotated[Optional[ProjectSelector], PropertyInfo(alias="projectSelector")]
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the
+ workspace.
+ """
+
+ tags: SequenceNotStr[str]
+ """Free-form labels on the framework."""
+
+
+class ProjectSelectorMatch(TypedDict, total=False):
+ property: Required[Literal["taskType", "riskLevel", "riskTotalScore", "name", "ownerId", "modelTypes"]]
+ """The project property to match against."""
+
+ value: Required[object]
+ """The value to match against.
+
+ Pass an array to match any of several values, or `null` to match projects where
+ the property is unset. Omit it for `exists` and `notExists`.
+ """
+
+ operator: str
+ """How to compare the project property with `value`.
+
+ One of `equals`, `notEquals`, `contains`, `notContains`, `startsWith`,
+ `endsWith`, `in`, `notIn`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
+ `lessThanOrEqual`, `equalsIgnoreCase`, `containsIgnoreCase`, `matches`,
+ `exists`, or `notExists`.
+ """
+
+
+class ProjectSelector(TypedDict, total=False):
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the workspace.
+ """
+
+ match: Optional[Iterable[ProjectSelectorMatch]]
+ """Match criteria, ANDed together."""
diff --git a/src/openlayer/types/governance/framework_create_response.py b/src/openlayer/types/governance/framework_create_response.py
new file mode 100644
index 00000000..4ff9942b
--- /dev/null
+++ b/src/openlayer/types/governance/framework_create_response.py
@@ -0,0 +1,195 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "FrameworkCreateResponse",
+ "Avatar",
+ "ProjectSelector",
+ "ProjectSelectorMatch",
+ "RuleStats",
+ "RuleStatsProjectCompletion",
+ "RuleStatsRuleResults",
+]
+
+
+class Avatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ProjectSelectorMatch(BaseModel):
+ property: Literal["taskType", "riskLevel", "riskTotalScore", "name", "ownerId", "modelTypes"]
+ """The project property to match against."""
+
+ value: object
+ """The value to match against.
+
+ Pass an array to match any of several values, or `null` to match projects where
+ the property is unset. Omit it for `exists` and `notExists`.
+ """
+
+ operator: Optional[str] = None
+ """How to compare the project property with `value`.
+
+ One of `equals`, `notEquals`, `contains`, `notContains`, `startsWith`,
+ `endsWith`, `in`, `notIn`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
+ `lessThanOrEqual`, `equalsIgnoreCase`, `containsIgnoreCase`, `matches`,
+ `exists`, or `notExists`.
+ """
+
+
+class ProjectSelector(BaseModel):
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the workspace.
+ """
+
+ match: Optional[List[ProjectSelectorMatch]] = None
+ """Match criteria, ANDed together."""
+
+
+class RuleStatsProjectCompletion(BaseModel):
+ """
+ How many of the framework's projects fall into each completion band, where a project's completion is the share of its rule results that are passing or skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's projects against each other, which says nothing about a single project.
+ """
+
+ high: int
+ """Projects at 80% completion or above."""
+
+ low: int
+ """Projects below 20% completion."""
+
+ mid: int
+ """Projects at or above 20% but below 80% completion."""
+
+
+class RuleStatsRuleResults(BaseModel):
+ """
+ Rule result counts by status for this framework, matching what `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a single project when the request also carries `projectId`.
+ """
+
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleStats(BaseModel):
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets `includeRuleStats=true`.
+ """
+
+ project_completion: RuleStatsProjectCompletion = FieldInfo(alias="projectCompletion")
+ """
+ How many of the framework's projects fall into each completion band, where a
+ project's completion is the share of its rule results that are passing or
+ skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's
+ projects against each other, which says nothing about a single project.
+ """
+
+ rule_results: RuleStatsRuleResults = FieldInfo(alias="ruleResults")
+ """
+ Rule result counts by status for this framework, matching what
+ `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a
+ single project when the request also carries `projectId`.
+ """
+
+
+class FrameworkCreateResponse(BaseModel):
+ id: str
+ """The framework id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+ tags: List[str]
+ """Free-form labels on the framework."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the framework belongs to."""
+
+ avatar: Optional[Avatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the framework. `null` for built-in frameworks."""
+
+ description: Optional[str] = None
+ """A short description of the framework."""
+
+ extended_description: Optional[Dict[str, object]] = FieldInfo(alias="extendedDescription", default=None)
+ """A longer, rich-text description, as a TipTap JSON document."""
+
+ href: Optional[str] = None
+ """A link to the external standard or regulation the framework is based on."""
+
+ immutable: Optional[bool] = None
+ """Whether the framework definition is managed by Openlayer and cannot be edited."""
+
+ project_selector: Optional[ProjectSelector] = FieldInfo(alias="projectSelector", default=None)
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the
+ workspace.
+ """
+
+ rule_stats: Optional[RuleStats] = FieldInfo(alias="ruleStats", default=None)
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets
+ `includeRuleStats=true`.
+ """
diff --git a/src/openlayer/types/governance/framework_export_params.py b/src/openlayer/types/governance/framework_export_params.py
new file mode 100644
index 00000000..43b661ae
--- /dev/null
+++ b/src/openlayer/types/governance/framework_export_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkExportParams"]
+
+
+class FrameworkExportParams(TypedDict, total=False):
+ project_id: Annotated[Optional[str], PropertyInfo(alias="projectId")]
+ """Scope the export to this project. It must belong to the framework."""
diff --git a/src/openlayer/types/governance/framework_export_response.py b/src/openlayer/types/governance/framework_export_response.py
new file mode 100644
index 00000000..5e4df9f1
--- /dev/null
+++ b/src/openlayer/types/governance/framework_export_response.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["FrameworkExportResponse"]
+
+
+class FrameworkExportResponse(BaseModel):
+ task_result_id: str = FieldInfo(alias="taskResultId")
+ """The background task id, for `GET /background-tasks/{taskId}`."""
+
+ task_result_url: str = FieldInfo(alias="taskResultUrl")
+ """The path to poll for this export's status and result.
+
+ Already `/v1`-prefixed, so it is relative to the API host rather than to the
+ `/v1` base url -- or just pass `taskResultId` to
+ `GET /background-tasks/{taskId}`.
+ """
diff --git a/src/openlayer/types/governance/framework_list_params.py b/src/openlayer/types/governance/framework_list_params.py
new file mode 100644
index 00000000..de459770
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_params.py
@@ -0,0 +1,55 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkListParams"]
+
+
+class FrameworkListParams(TypedDict, total=False):
+ asc: bool
+ """Whether to sort in ascending order."""
+
+ completion_operator: Annotated[Literal["is", ">", ">=", "<", "<=", "!="], PropertyInfo(alias="completionOperator")]
+ """How to compare each framework's completion percentage with `completionValue`.
+
+ Must be sent together with `completionValue`.
+ """
+
+ completion_value: Annotated[int, PropertyInfo(alias="completionValue")]
+ """The completion percentage to compare against, from 0 to 100."""
+
+ enabled: bool
+ """Only include frameworks that are enabled (or disabled)."""
+
+ include_rule_stats: Annotated[bool, PropertyInfo(alias="includeRuleStats")]
+ """
+ Whether to include a `ruleStats` object on each framework, with its rule result
+ status counts and its per-project completion buckets. Computed over the returned
+ page only.
+ """
+
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
+
+ project_id: Annotated[str, PropertyInfo(alias="projectId")]
+ """Only include items that apply to this project."""
+
+ search_query: Annotated[str, PropertyInfo(alias="searchQuery")]
+ """Filter by a free-text search over names and descriptions."""
+
+ sort_column: Annotated[
+ Literal["name", "enabled", "dateCreated", "dateUpdated", "overallCompletion", "projectCompletionBuckets"],
+ PropertyInfo(alias="sortColumn"),
+ ]
+ """The column to sort on."""
+
+ tags: SequenceNotStr[str]
+ """Only include frameworks carrying all of these tags."""
diff --git a/src/openlayer/types/governance/framework_list_project_rule_stats_params.py b/src/openlayer/types/governance/framework_list_project_rule_stats_params.py
new file mode 100644
index 00000000..3070c462
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_project_rule_stats_params.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkListProjectRuleStatsParams"]
+
+
+class FrameworkListProjectRuleStatsParams(TypedDict, total=False):
+ asc: bool
+ """Whether to sort in ascending order."""
+
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
+
+ sort_column: Annotated[
+ Literal[
+ "projectName",
+ "total",
+ "overallCompletion",
+ "totalPassing",
+ "totalFailing",
+ "totalSkipped",
+ "totalRunning",
+ "totalError",
+ "totalPending",
+ "totalDueSoon",
+ ],
+ PropertyInfo(alias="sortColumn"),
+ ]
+ """The column to sort on."""
diff --git a/src/openlayer/types/governance/framework_list_project_rule_stats_response.py b/src/openlayer/types/governance/framework_list_project_rule_stats_response.py
new file mode 100644
index 00000000..7a4e7749
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_project_rule_stats_response.py
@@ -0,0 +1,117 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "FrameworkListProjectRuleStatsResponse",
+ "Item",
+ "ItemByRuleType",
+ "ItemByRuleTypeEvidence",
+ "ItemByRuleTypePlatform",
+]
+
+
+class ItemByRuleTypeEvidence(BaseModel):
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class ItemByRuleTypePlatform(BaseModel):
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class ItemByRuleType(BaseModel):
+ """The same counts, broken down by the type of the rule each result belongs to."""
+
+ evidence: Optional[ItemByRuleTypeEvidence] = None
+
+ platform: Optional[ItemByRuleTypePlatform] = None
+
+
+class Item(BaseModel):
+ project_id: str = FieldInfo(alias="projectId")
+ """The project id."""
+
+ project_name: str = FieldInfo(alias="projectName")
+ """The project name."""
+
+ task_type: str = FieldInfo(alias="taskType")
+ """The project's task type."""
+
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+ by_rule_type: Optional[ItemByRuleType] = FieldInfo(alias="byRuleType", default=None)
+ """The same counts, broken down by the type of the rule each result belongs to."""
+
+
+class FrameworkListProjectRuleStatsResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/framework_list_projects_params.py b/src/openlayer/types/governance/framework_list_projects_params.py
new file mode 100644
index 00000000..a121ac1f
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_projects_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkListProjectsParams"]
+
+
+class FrameworkListProjectsParams(TypedDict, total=False):
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
diff --git a/src/openlayer/types/governance/framework_list_projects_response.py b/src/openlayer/types/governance/framework_list_projects_response.py
new file mode 100644
index 00000000..c6f65e46
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_projects_response.py
@@ -0,0 +1,113 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["FrameworkListProjectsResponse", "Item", "ItemLinks", "ItemGitRepo"]
+
+
+class ItemLinks(BaseModel):
+ """Links to the project."""
+
+ app: str
+
+
+class ItemGitRepo(BaseModel):
+ id: str
+
+ date_connected: datetime = FieldInfo(alias="dateConnected")
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+
+ git_account_id: str = FieldInfo(alias="gitAccountId")
+
+ git_id: int = FieldInfo(alias="gitId")
+
+ name: str
+
+ private: bool
+
+ project_id: str = FieldInfo(alias="projectId")
+
+ slug: str
+
+ url: str
+
+ branch: Optional[str] = None
+
+ root_dir: Optional[str] = FieldInfo(alias="rootDir", default=None)
+
+
+class Item(BaseModel):
+ id: str
+ """The project id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The project creator id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The project creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The project last updated date."""
+
+ development_goal_count: int = FieldInfo(alias="developmentGoalCount")
+ """The number of tests in the development mode of the project."""
+
+ goal_count: int = FieldInfo(alias="goalCount")
+ """The total number of tests in the project."""
+
+ inference_pipeline_count: int = FieldInfo(alias="inferencePipelineCount")
+ """The number of inference pipelines in the project."""
+
+ links: ItemLinks
+ """Links to the project."""
+
+ monitoring_goal_count: int = FieldInfo(alias="monitoringGoalCount")
+ """The number of tests in the monitoring mode of the project."""
+
+ name: str
+ """The project name."""
+
+ source: Optional[Literal["web", "api"]] = None
+ """The source of the project."""
+
+ task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
+ alias="taskType"
+ )
+ """The task type of the project."""
+
+ version_count: int = FieldInfo(alias="versionCount")
+ """The number of versions (commits) in the project."""
+
+ workspace_id: Optional[str] = FieldInfo(alias="workspaceId", default=None)
+ """The workspace id."""
+
+ data_retention_days: Optional[int] = FieldInfo(alias="dataRetentionDays", default=None)
+ """Number of days to retain monitoring data for this project.
+
+ Null means data is retained indefinitely.
+ """
+
+ description: Optional[str] = None
+ """The project description."""
+
+ git_repo: Optional[ItemGitRepo] = FieldInfo(alias="gitRepo", default=None)
+
+ api_model_developer: Optional[str] = FieldInfo(alias="modelDeveloper", default=None)
+ """Who developed the model used in this project."""
+
+ api_model_types: Optional[List[str]] = FieldInfo(alias="modelTypes", default=None)
+ """The kinds of model used in this project."""
+
+ purpose: Optional[str] = None
+ """What the system in this project is intended to do."""
+
+
+class FrameworkListProjectsResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/framework_list_response.py b/src/openlayer/types/governance/framework_list_response.py
new file mode 100644
index 00000000..f917e020
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_response.py
@@ -0,0 +1,200 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "FrameworkListResponse",
+ "Item",
+ "ItemAvatar",
+ "ItemProjectSelector",
+ "ItemProjectSelectorMatch",
+ "ItemRuleStats",
+ "ItemRuleStatsProjectCompletion",
+ "ItemRuleStatsRuleResults",
+]
+
+
+class ItemAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ItemProjectSelectorMatch(BaseModel):
+ property: Literal["taskType", "riskLevel", "riskTotalScore", "name", "ownerId", "modelTypes"]
+ """The project property to match against."""
+
+ value: object
+ """The value to match against.
+
+ Pass an array to match any of several values, or `null` to match projects where
+ the property is unset. Omit it for `exists` and `notExists`.
+ """
+
+ operator: Optional[str] = None
+ """How to compare the project property with `value`.
+
+ One of `equals`, `notEquals`, `contains`, `notContains`, `startsWith`,
+ `endsWith`, `in`, `notIn`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
+ `lessThanOrEqual`, `equalsIgnoreCase`, `containsIgnoreCase`, `matches`,
+ `exists`, or `notExists`.
+ """
+
+
+class ItemProjectSelector(BaseModel):
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the workspace.
+ """
+
+ match: Optional[List[ItemProjectSelectorMatch]] = None
+ """Match criteria, ANDed together."""
+
+
+class ItemRuleStatsProjectCompletion(BaseModel):
+ """
+ How many of the framework's projects fall into each completion band, where a project's completion is the share of its rule results that are passing or skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's projects against each other, which says nothing about a single project.
+ """
+
+ high: int
+ """Projects at 80% completion or above."""
+
+ low: int
+ """Projects below 20% completion."""
+
+ mid: int
+ """Projects at or above 20% but below 80% completion."""
+
+
+class ItemRuleStatsRuleResults(BaseModel):
+ """
+ Rule result counts by status for this framework, matching what `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a single project when the request also carries `projectId`.
+ """
+
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class ItemRuleStats(BaseModel):
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets `includeRuleStats=true`.
+ """
+
+ project_completion: ItemRuleStatsProjectCompletion = FieldInfo(alias="projectCompletion")
+ """
+ How many of the framework's projects fall into each completion band, where a
+ project's completion is the share of its rule results that are passing or
+ skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's
+ projects against each other, which says nothing about a single project.
+ """
+
+ rule_results: ItemRuleStatsRuleResults = FieldInfo(alias="ruleResults")
+ """
+ Rule result counts by status for this framework, matching what
+ `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a
+ single project when the request also carries `projectId`.
+ """
+
+
+class Item(BaseModel):
+ id: str
+ """The framework id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+ tags: List[str]
+ """Free-form labels on the framework."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the framework belongs to."""
+
+ avatar: Optional[ItemAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the framework. `null` for built-in frameworks."""
+
+ description: Optional[str] = None
+ """A short description of the framework."""
+
+ extended_description: Optional[Dict[str, object]] = FieldInfo(alias="extendedDescription", default=None)
+ """A longer, rich-text description, as a TipTap JSON document."""
+
+ href: Optional[str] = None
+ """A link to the external standard or regulation the framework is based on."""
+
+ immutable: Optional[bool] = None
+ """Whether the framework definition is managed by Openlayer and cannot be edited."""
+
+ project_selector: Optional[ItemProjectSelector] = FieldInfo(alias="projectSelector", default=None)
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the
+ workspace.
+ """
+
+ rule_stats: Optional[ItemRuleStats] = FieldInfo(alias="ruleStats", default=None)
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets
+ `includeRuleStats=true`.
+ """
+
+
+class FrameworkListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/framework_list_rules_params.py b/src/openlayer/types/governance/framework_list_rules_params.py
new file mode 100644
index 00000000..618af53a
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_rules_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkListRulesParams"]
+
+
+class FrameworkListRulesParams(TypedDict, total=False):
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
diff --git a/src/openlayer/types/governance/framework_list_rules_response.py b/src/openlayer/types/governance/framework_list_rules_response.py
new file mode 100644
index 00000000..53698c24
--- /dev/null
+++ b/src/openlayer/types/governance/framework_list_rules_response.py
@@ -0,0 +1,242 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "FrameworkListRulesResponse",
+ "Item",
+ "ItemFramework",
+ "ItemFrameworkAvatar",
+ "ItemResult",
+ "ItemResultBlockedBy",
+ "ItemResultBlocking",
+ "ItemResultsSummary",
+ "ItemTag",
+]
+
+
+class ItemFrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ItemFramework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[ItemFrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ItemResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResult(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ItemResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ItemResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ItemResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class ItemTag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class Item(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[ItemFramework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[ItemResult]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ItemResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[ItemTag]] = None
+ """The rule tags associated with the rule."""
+
+
+class FrameworkListRulesResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/framework_retrieve_response.py b/src/openlayer/types/governance/framework_retrieve_response.py
new file mode 100644
index 00000000..6c3f37da
--- /dev/null
+++ b/src/openlayer/types/governance/framework_retrieve_response.py
@@ -0,0 +1,195 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "FrameworkRetrieveResponse",
+ "Avatar",
+ "ProjectSelector",
+ "ProjectSelectorMatch",
+ "RuleStats",
+ "RuleStatsProjectCompletion",
+ "RuleStatsRuleResults",
+]
+
+
+class Avatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ProjectSelectorMatch(BaseModel):
+ property: Literal["taskType", "riskLevel", "riskTotalScore", "name", "ownerId", "modelTypes"]
+ """The project property to match against."""
+
+ value: object
+ """The value to match against.
+
+ Pass an array to match any of several values, or `null` to match projects where
+ the property is unset. Omit it for `exists` and `notExists`.
+ """
+
+ operator: Optional[str] = None
+ """How to compare the project property with `value`.
+
+ One of `equals`, `notEquals`, `contains`, `notContains`, `startsWith`,
+ `endsWith`, `in`, `notIn`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
+ `lessThanOrEqual`, `equalsIgnoreCase`, `containsIgnoreCase`, `matches`,
+ `exists`, or `notExists`.
+ """
+
+
+class ProjectSelector(BaseModel):
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the workspace.
+ """
+
+ match: Optional[List[ProjectSelectorMatch]] = None
+ """Match criteria, ANDed together."""
+
+
+class RuleStatsProjectCompletion(BaseModel):
+ """
+ How many of the framework's projects fall into each completion band, where a project's completion is the share of its rule results that are passing or skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's projects against each other, which says nothing about a single project.
+ """
+
+ high: int
+ """Projects at 80% completion or above."""
+
+ low: int
+ """Projects below 20% completion."""
+
+ mid: int
+ """Projects at or above 20% but below 80% completion."""
+
+
+class RuleStatsRuleResults(BaseModel):
+ """
+ Rule result counts by status for this framework, matching what `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a single project when the request also carries `projectId`.
+ """
+
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleStats(BaseModel):
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets `includeRuleStats=true`.
+ """
+
+ project_completion: RuleStatsProjectCompletion = FieldInfo(alias="projectCompletion")
+ """
+ How many of the framework's projects fall into each completion band, where a
+ project's completion is the share of its rule results that are passing or
+ skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's
+ projects against each other, which says nothing about a single project.
+ """
+
+ rule_results: RuleStatsRuleResults = FieldInfo(alias="ruleResults")
+ """
+ Rule result counts by status for this framework, matching what
+ `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a
+ single project when the request also carries `projectId`.
+ """
+
+
+class FrameworkRetrieveResponse(BaseModel):
+ id: str
+ """The framework id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+ tags: List[str]
+ """Free-form labels on the framework."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the framework belongs to."""
+
+ avatar: Optional[Avatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the framework. `null` for built-in frameworks."""
+
+ description: Optional[str] = None
+ """A short description of the framework."""
+
+ extended_description: Optional[Dict[str, object]] = FieldInfo(alias="extendedDescription", default=None)
+ """A longer, rich-text description, as a TipTap JSON document."""
+
+ href: Optional[str] = None
+ """A link to the external standard or regulation the framework is based on."""
+
+ immutable: Optional[bool] = None
+ """Whether the framework definition is managed by Openlayer and cannot be edited."""
+
+ project_selector: Optional[ProjectSelector] = FieldInfo(alias="projectSelector", default=None)
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the
+ workspace.
+ """
+
+ rule_stats: Optional[RuleStats] = FieldInfo(alias="ruleStats", default=None)
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets
+ `includeRuleStats=true`.
+ """
diff --git a/src/openlayer/types/governance/framework_update_params.py b/src/openlayer/types/governance/framework_update_params.py
new file mode 100644
index 00000000..28e1ad62
--- /dev/null
+++ b/src/openlayer/types/governance/framework_update_params.py
@@ -0,0 +1,84 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["FrameworkUpdateParams", "Avatar", "ProjectSelector", "ProjectSelectorMatch"]
+
+
+class FrameworkUpdateParams(TypedDict, total=False):
+ avatar: Optional[Avatar]
+ """The icon shown for the framework."""
+
+ description: Optional[str]
+ """A short description of the framework."""
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ extended_description: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="extendedDescription")]
+ """A longer, rich-text description, as a TipTap JSON document."""
+
+ href: Optional[str]
+ """A link to the external standard or regulation the framework is based on."""
+
+ name: str
+ """The framework name."""
+
+ project_selector: Annotated[Optional[ProjectSelector], PropertyInfo(alias="projectSelector")]
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the
+ workspace.
+ """
+
+ tags: SequenceNotStr[str]
+ """Free-form labels on the framework."""
+
+
+class Avatar(TypedDict, total=False):
+ """The icon shown for the framework."""
+
+ type: Required[Literal["emoji", "imageUrl", "builtinImage"]]
+
+ value: Required[str]
+
+
+class ProjectSelectorMatch(TypedDict, total=False):
+ property: Required[Literal["taskType", "riskLevel", "riskTotalScore", "name", "ownerId", "modelTypes"]]
+ """The project property to match against."""
+
+ value: Required[object]
+ """The value to match against.
+
+ Pass an array to match any of several values, or `null` to match projects where
+ the property is unset. Omit it for `exists` and `notExists`.
+ """
+
+ operator: str
+ """How to compare the project property with `value`.
+
+ One of `equals`, `notEquals`, `contains`, `notContains`, `startsWith`,
+ `endsWith`, `in`, `notIn`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
+ `lessThanOrEqual`, `equalsIgnoreCase`, `containsIgnoreCase`, `matches`,
+ `exists`, or `notExists`.
+ """
+
+
+class ProjectSelector(TypedDict, total=False):
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the workspace.
+ """
+
+ match: Optional[Iterable[ProjectSelectorMatch]]
+ """Match criteria, ANDed together."""
diff --git a/src/openlayer/types/governance/framework_update_response.py b/src/openlayer/types/governance/framework_update_response.py
new file mode 100644
index 00000000..ff9de4cc
--- /dev/null
+++ b/src/openlayer/types/governance/framework_update_response.py
@@ -0,0 +1,195 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "FrameworkUpdateResponse",
+ "Avatar",
+ "ProjectSelector",
+ "ProjectSelectorMatch",
+ "RuleStats",
+ "RuleStatsProjectCompletion",
+ "RuleStatsRuleResults",
+]
+
+
+class Avatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ProjectSelectorMatch(BaseModel):
+ property: Literal["taskType", "riskLevel", "riskTotalScore", "name", "ownerId", "modelTypes"]
+ """The project property to match against."""
+
+ value: object
+ """The value to match against.
+
+ Pass an array to match any of several values, or `null` to match projects where
+ the property is unset. Omit it for `exists` and `notExists`.
+ """
+
+ operator: Optional[str] = None
+ """How to compare the project property with `value`.
+
+ One of `equals`, `notEquals`, `contains`, `notContains`, `startsWith`,
+ `endsWith`, `in`, `notIn`, `greaterThan`, `greaterThanOrEqual`, `lessThan`,
+ `lessThanOrEqual`, `equalsIgnoreCase`, `containsIgnoreCase`, `matches`,
+ `exists`, or `notExists`.
+ """
+
+
+class ProjectSelector(BaseModel):
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the workspace.
+ """
+
+ match: Optional[List[ProjectSelectorMatch]] = None
+ """Match criteria, ANDed together."""
+
+
+class RuleStatsProjectCompletion(BaseModel):
+ """
+ How many of the framework's projects fall into each completion band, where a project's completion is the share of its rule results that are passing or skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's projects against each other, which says nothing about a single project.
+ """
+
+ high: int
+ """Projects at 80% completion or above."""
+
+ low: int
+ """Projects below 20% completion."""
+
+ mid: int
+ """Projects at or above 20% but below 80% completion."""
+
+
+class RuleStatsRuleResults(BaseModel):
+ """
+ Rule result counts by status for this framework, matching what `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a single project when the request also carries `projectId`.
+ """
+
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleStats(BaseModel):
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets `includeRuleStats=true`.
+ """
+
+ project_completion: RuleStatsProjectCompletion = FieldInfo(alias="projectCompletion")
+ """
+ How many of the framework's projects fall into each completion band, where a
+ project's completion is the share of its rule results that are passing or
+ skipped. Projects with no evaluated results count as `low`.
+
+ Zeroed when the request carries `projectId`: the bands compare a framework's
+ projects against each other, which says nothing about a single project.
+ """
+
+ rule_results: RuleStatsRuleResults = FieldInfo(alias="ruleResults")
+ """
+ Rule result counts by status for this framework, matching what
+ `/workspaces/{workspaceId}/rule-stats?frameworkId=` reports. Narrowed to a
+ single project when the request also carries `projectId`.
+ """
+
+
+class FrameworkUpdateResponse(BaseModel):
+ id: str
+ """The framework id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+ tags: List[str]
+ """Free-form labels on the framework."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the framework belongs to."""
+
+ avatar: Optional[Avatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the framework. `null` for built-in frameworks."""
+
+ description: Optional[str] = None
+ """A short description of the framework."""
+
+ extended_description: Optional[Dict[str, object]] = FieldInfo(alias="extendedDescription", default=None)
+ """A longer, rich-text description, as a TipTap JSON document."""
+
+ href: Optional[str] = None
+ """A link to the external standard or regulation the framework is based on."""
+
+ immutable: Optional[bool] = None
+ """Whether the framework definition is managed by Openlayer and cannot be edited."""
+
+ project_selector: Optional[ProjectSelector] = FieldInfo(alias="projectSelector", default=None)
+ """Determines which projects the framework applies to.
+
+ An empty or `null` `match` array applies the framework to every project in the
+ workspace.
+ """
+
+ rule_stats: Optional[RuleStats] = FieldInfo(alias="ruleStats", default=None)
+ """Compliance roll-up for the framework.
+
+ Present only on `GET /workspaces/{workspaceId}/frameworks` when the request sets
+ `includeRuleStats=true`.
+ """
diff --git a/src/openlayer/types/governance/frameworks/__init__.py b/src/openlayer/types/governance/frameworks/__init__.py
new file mode 100644
index 00000000..47f4d345
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/__init__.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .document_list_params import DocumentListParams as DocumentListParams
+from .document_list_response import DocumentListResponse as DocumentListResponse
+from .section_list_rules_params import SectionListRulesParams as SectionListRulesParams
+from .document_retrieve_response import DocumentRetrieveResponse as DocumentRetrieveResponse
+from .section_list_rules_response import SectionListRulesResponse as SectionListRulesResponse
+from .subsection_list_rules_params import SubsectionListRulesParams as SubsectionListRulesParams
+from .subsection_list_rules_response import SubsectionListRulesResponse as SubsectionListRulesResponse
diff --git a/src/openlayer/types/governance/frameworks/document_list_params.py b/src/openlayer/types/governance/frameworks/document_list_params.py
new file mode 100644
index 00000000..91509d9e
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/document_list_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["DocumentListParams"]
+
+
+class DocumentListParams(TypedDict, total=False):
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
diff --git a/src/openlayer/types/governance/frameworks/document_list_response.py b/src/openlayer/types/governance/frameworks/document_list_response.py
new file mode 100644
index 00000000..f88a8a49
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/document_list_response.py
@@ -0,0 +1,217 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "DocumentListResponse",
+ "Item",
+ "ItemSection",
+ "ItemSectionRule",
+ "ItemSectionSubsection",
+ "ItemSectionSubsectionRule",
+]
+
+
+class ItemSectionRule(BaseModel):
+ id: str
+ """The rule id."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ date_created: Optional[datetime] = FieldInfo(alias="dateCreated", default=None)
+ """The creation date."""
+
+ date_updated: Optional[datetime] = FieldInfo(alias="dateUpdated", default=None)
+ """The last update date."""
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+
+class ItemSectionSubsectionRule(BaseModel):
+ id: str
+ """The rule id."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ date_created: Optional[datetime] = FieldInfo(alias="dateCreated", default=None)
+ """The creation date."""
+
+ date_updated: Optional[datetime] = FieldInfo(alias="dateUpdated", default=None)
+ """The last update date."""
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+
+class ItemSectionSubsection(BaseModel):
+ id: str
+ """The subsection id."""
+
+ number: str
+ """The subsection number as it appears in the source standard."""
+
+ section_id: str = FieldInfo(alias="sectionId")
+ """The section the subsection belongs to."""
+
+ sort_order: int = FieldInfo(alias="sortOrder")
+ """The position of the subsection within its section."""
+
+ title: str
+ """The subsection title."""
+
+ rule_count: Optional[int] = FieldInfo(alias="ruleCount", default=None)
+ """How many rules are linked to this subsection."""
+
+ rules: Optional[List[ItemSectionSubsectionRule]] = None
+ """The rules linked to this subsection."""
+
+ text: Optional[str] = None
+ """The subsection text. This is the requirement your rules are mapped against."""
+
+
+class ItemSection(BaseModel):
+ id: str
+ """The section id."""
+
+ document_id: str = FieldInfo(alias="documentId")
+ """The document the section belongs to."""
+
+ number: str
+ """The section number as it appears in the source standard."""
+
+ sort_order: int = FieldInfo(alias="sortOrder")
+ """The position of the section within the document."""
+
+ title: str
+ """The section title."""
+
+ rule_count: Optional[int] = FieldInfo(alias="ruleCount", default=None)
+ """How many rules are linked to this section, including its subsections.
+
+ Use it to decide whether to fetch the section's rules.
+ """
+
+ rules: Optional[List[ItemSectionRule]] = None
+ """The rules linked directly to this section."""
+
+ subsections: Optional[List[ItemSectionSubsection]] = None
+ """The section's subsections, in display order."""
+
+ text: Optional[str] = None
+ """The section text."""
+
+
+class Item(BaseModel):
+ id: str
+ """The document id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ framework_id: str = FieldInfo(alias="frameworkId")
+ """The framework the document belongs to."""
+
+ title: str
+ """The document title."""
+
+ sections: Optional[List[ItemSection]] = None
+ """The document's sections, in display order.
+
+ Only returned when retrieving a single document.
+ """
+
+
+class DocumentListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/frameworks/document_retrieve_response.py b/src/openlayer/types/governance/frameworks/document_retrieve_response.py
new file mode 100644
index 00000000..cf22e299
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/document_retrieve_response.py
@@ -0,0 +1,206 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = ["DocumentRetrieveResponse", "Section", "SectionRule", "SectionSubsection", "SectionSubsectionRule"]
+
+
+class SectionRule(BaseModel):
+ id: str
+ """The rule id."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ date_created: Optional[datetime] = FieldInfo(alias="dateCreated", default=None)
+ """The creation date."""
+
+ date_updated: Optional[datetime] = FieldInfo(alias="dateUpdated", default=None)
+ """The last update date."""
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+
+class SectionSubsectionRule(BaseModel):
+ id: str
+ """The rule id."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ date_created: Optional[datetime] = FieldInfo(alias="dateCreated", default=None)
+ """The creation date."""
+
+ date_updated: Optional[datetime] = FieldInfo(alias="dateUpdated", default=None)
+ """The last update date."""
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+
+class SectionSubsection(BaseModel):
+ id: str
+ """The subsection id."""
+
+ number: str
+ """The subsection number as it appears in the source standard."""
+
+ section_id: str = FieldInfo(alias="sectionId")
+ """The section the subsection belongs to."""
+
+ sort_order: int = FieldInfo(alias="sortOrder")
+ """The position of the subsection within its section."""
+
+ title: str
+ """The subsection title."""
+
+ rule_count: Optional[int] = FieldInfo(alias="ruleCount", default=None)
+ """How many rules are linked to this subsection."""
+
+ rules: Optional[List[SectionSubsectionRule]] = None
+ """The rules linked to this subsection."""
+
+ text: Optional[str] = None
+ """The subsection text. This is the requirement your rules are mapped against."""
+
+
+class Section(BaseModel):
+ id: str
+ """The section id."""
+
+ document_id: str = FieldInfo(alias="documentId")
+ """The document the section belongs to."""
+
+ number: str
+ """The section number as it appears in the source standard."""
+
+ sort_order: int = FieldInfo(alias="sortOrder")
+ """The position of the section within the document."""
+
+ title: str
+ """The section title."""
+
+ rule_count: Optional[int] = FieldInfo(alias="ruleCount", default=None)
+ """How many rules are linked to this section, including its subsections.
+
+ Use it to decide whether to fetch the section's rules.
+ """
+
+ rules: Optional[List[SectionRule]] = None
+ """The rules linked directly to this section."""
+
+ subsections: Optional[List[SectionSubsection]] = None
+ """The section's subsections, in display order."""
+
+ text: Optional[str] = None
+ """The section text."""
+
+
+class DocumentRetrieveResponse(BaseModel):
+ id: str
+ """The document id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ framework_id: str = FieldInfo(alias="frameworkId")
+ """The framework the document belongs to."""
+
+ title: str
+ """The document title."""
+
+ sections: Optional[List[Section]] = None
+ """The document's sections, in display order.
+
+ Only returned when retrieving a single document.
+ """
diff --git a/src/openlayer/types/governance/frameworks/section_list_rules_params.py b/src/openlayer/types/governance/frameworks/section_list_rules_params.py
new file mode 100644
index 00000000..aa310f0c
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/section_list_rules_params.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["SectionListRulesParams"]
+
+
+class SectionListRulesParams(TypedDict, total=False):
+ framework_id: Required[Annotated[str, PropertyInfo(alias="frameworkId")]]
+
+ include_results: Annotated[bool, PropertyInfo(alias="includeResults")]
+ """Whether to include each rule's results inline, in a `results` array."""
+
+ include_subsection_rules: Annotated[bool, PropertyInfo(alias="includeSubsectionRules")]
+ """Whether to also include the rules mapped to the section's subsections."""
+
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
+
+ project_id: Annotated[str, PropertyInfo(alias="projectId")]
+ """Only include items that apply to this project."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """Only include items whose rule result has this compliance status."""
diff --git a/src/openlayer/types/governance/frameworks/section_list_rules_response.py b/src/openlayer/types/governance/frameworks/section_list_rules_response.py
new file mode 100644
index 00000000..75b829b5
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/section_list_rules_response.py
@@ -0,0 +1,242 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "SectionListRulesResponse",
+ "Item",
+ "ItemFramework",
+ "ItemFrameworkAvatar",
+ "ItemResult",
+ "ItemResultBlockedBy",
+ "ItemResultBlocking",
+ "ItemResultsSummary",
+ "ItemTag",
+]
+
+
+class ItemFrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ItemFramework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[ItemFrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ItemResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResult(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ItemResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ItemResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ItemResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class ItemTag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class Item(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[ItemFramework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[ItemResult]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ItemResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[ItemTag]] = None
+ """The rule tags associated with the rule."""
+
+
+class SectionListRulesResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/frameworks/subsection_list_rules_params.py b/src/openlayer/types/governance/frameworks/subsection_list_rules_params.py
new file mode 100644
index 00000000..7d88425c
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/subsection_list_rules_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["SubsectionListRulesParams"]
+
+
+class SubsectionListRulesParams(TypedDict, total=False):
+ framework_id: Required[Annotated[str, PropertyInfo(alias="frameworkId")]]
+
+ include_results: Annotated[bool, PropertyInfo(alias="includeResults")]
+ """Whether to include each rule's results inline, in a `results` array."""
+
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
+
+ project_id: Annotated[str, PropertyInfo(alias="projectId")]
+ """Only include items that apply to this project."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """Only include items whose rule result has this compliance status."""
diff --git a/src/openlayer/types/governance/frameworks/subsection_list_rules_response.py b/src/openlayer/types/governance/frameworks/subsection_list_rules_response.py
new file mode 100644
index 00000000..1b934a4b
--- /dev/null
+++ b/src/openlayer/types/governance/frameworks/subsection_list_rules_response.py
@@ -0,0 +1,242 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "SubsectionListRulesResponse",
+ "Item",
+ "ItemFramework",
+ "ItemFrameworkAvatar",
+ "ItemResult",
+ "ItemResultBlockedBy",
+ "ItemResultBlocking",
+ "ItemResultsSummary",
+ "ItemTag",
+]
+
+
+class ItemFrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ItemFramework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[ItemFrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ItemResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResult(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ItemResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ItemResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ItemResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class ItemTag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class Item(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[ItemFramework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[ItemResult]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ItemResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[ItemTag]] = None
+ """The rule tags associated with the rule."""
+
+
+class SubsectionListRulesResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/rule_create_params.py b/src/openlayer/types/governance/rule_create_params.py
new file mode 100644
index 00000000..bb660fe7
--- /dev/null
+++ b/src/openlayer/types/governance/rule_create_params.py
@@ -0,0 +1,66 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleCreateParams"]
+
+
+class RuleCreateParams(TypedDict, total=False):
+ name: Required[str]
+ """The rule name."""
+
+ scope: Required[Literal["project", "workspace"]]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Required[Literal["platform", "evidence"]]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ assignee_id: Annotated[Optional[str], PropertyInfo(alias="assigneeId")]
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="automationParams")]
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Annotated[Optional[str], PropertyInfo(alias="automationType")]
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: bool
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str]
+ """What the rule requires."""
+
+ evidence_type: Annotated[
+ Optional[Literal["document", "text", "url", "categoryValue"]], PropertyInfo(alias="evidenceType")
+ ]
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ renewal_cadence_days: Annotated[Optional[int], PropertyInfo(alias="renewalCadenceDays")]
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ tag_ids: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="tagIds")]
+ """The ids of the rule tags to associate with the rule.
+
+ Replaces the rule's tags. Read them back from `tags`, and list the tags
+ available in the workspace with `GET /workspaces/{workspaceId}/rule-tags`.
+ """
diff --git a/src/openlayer/types/governance/rule_create_response.py b/src/openlayer/types/governance/rule_create_response.py
new file mode 100644
index 00000000..3f2475dd
--- /dev/null
+++ b/src/openlayer/types/governance/rule_create_response.py
@@ -0,0 +1,237 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RuleCreateResponse",
+ "Framework",
+ "FrameworkAvatar",
+ "Result",
+ "ResultBlockedBy",
+ "ResultBlocking",
+ "ResultsSummary",
+ "Tag",
+]
+
+
+class FrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class Framework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[FrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class Result(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class Tag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class RuleCreateResponse(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[Framework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[Result]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[Tag]] = None
+ """The rule tags associated with the rule."""
diff --git a/src/openlayer/types/governance/rule_list_params.py b/src/openlayer/types/governance/rule_list_params.py
new file mode 100644
index 00000000..85d82c8c
--- /dev/null
+++ b/src/openlayer/types/governance/rule_list_params.py
@@ -0,0 +1,71 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleListParams"]
+
+
+class RuleListParams(TypedDict, total=False):
+ asc: bool
+ """Whether to sort in ascending order."""
+
+ assignee_id: Annotated[str, PropertyInfo(alias="assigneeId")]
+ """Only include rules assigned to this user."""
+
+ deactivated: bool
+ """Only include rules that are deactivated (or active)."""
+
+ enabled_framework_only: Annotated[bool, PropertyInfo(alias="enabledFrameworkOnly")]
+ """Only include items belonging to at least one enabled framework."""
+
+ framework_id: Annotated[str, PropertyInfo(alias="frameworkId")]
+ """Only include items belonging to this framework."""
+
+ group: Literal["open", "excluded", "done"]
+ """Only include rules in one bucket of the compliance workflow.
+
+ `open` covers rules that still need attention, `done` covers rules that are
+ fully satisfied, and `excluded` covers rules that have been deactivated.
+ """
+
+ include_results: Annotated[bool, PropertyInfo(alias="includeResults")]
+ """Whether to include each rule's results inline, in a `results` array."""
+
+ include_unframed: Annotated[bool, PropertyInfo(alias="includeUnframed")]
+ """Whether to include rules that are not part of any framework."""
+
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
+
+ project_id: Annotated[str, PropertyInfo(alias="projectId")]
+ """Only include items that apply to this project."""
+
+ scope: Literal["project", "workspace"]
+ """Only include rules with this scope."""
+
+ search_query: Annotated[str, PropertyInfo(alias="searchQuery")]
+ """Filter by a free-text search over names and descriptions."""
+
+ sort_by: Annotated[Literal["name", "status", "frameworks", "scope", "dateCreated"], PropertyInfo(alias="sortBy")]
+ """The field to sort on."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """Only include items whose rule result has this compliance status."""
+
+ tags: SequenceNotStr[str]
+ """Only include rules carrying all of these rule tags.
+
+ Pass tag ids, which you can look up with
+ [List rule tags](/api-reference/rest/governance/list-rule-tags).
+ """
+
+ type: Literal["platform", "evidence"]
+ """Only include rules of this type."""
diff --git a/src/openlayer/types/governance/rule_list_response.py b/src/openlayer/types/governance/rule_list_response.py
new file mode 100644
index 00000000..582fb42a
--- /dev/null
+++ b/src/openlayer/types/governance/rule_list_response.py
@@ -0,0 +1,242 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RuleListResponse",
+ "Item",
+ "ItemFramework",
+ "ItemFrameworkAvatar",
+ "ItemResult",
+ "ItemResultBlockedBy",
+ "ItemResultBlocking",
+ "ItemResultsSummary",
+ "ItemTag",
+]
+
+
+class ItemFrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class ItemFramework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[ItemFrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ItemResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemResult(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ItemResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ItemResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ItemResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class ItemTag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class Item(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[ItemFramework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[ItemResult]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ItemResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[ItemTag]] = None
+ """The rule tags associated with the rule."""
+
+
+class RuleListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/rule_result_create_evidence_params.py b/src/openlayer/types/governance/rule_result_create_evidence_params.py
new file mode 100644
index 00000000..a049e054
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_create_evidence_params.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleResultCreateEvidenceParams"]
+
+
+class RuleResultCreateEvidenceParams(TypedDict, total=False):
+ description: Optional[str]
+ """A description of what the evidence shows."""
+
+ name: Optional[str]
+ """The evidence name."""
+
+ storage_uri: Annotated[Optional[str], PropertyInfo(alias="storageUri")]
+ """Where the uploaded file is stored.
+
+ Set when the rule's `evidenceType` is `document`.
+ """
+
+ text: Optional[str]
+ """The evidence text. Set when the rule's `evidenceType` is `text`."""
+
+ url: Optional[str]
+ """A link to the evidence. Set when the rule's `evidenceType` is `url`."""
diff --git a/src/openlayer/types/governance/rule_result_create_evidence_response.py b/src/openlayer/types/governance/rule_result_create_evidence_response.py
new file mode 100644
index 00000000..92e21736
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_create_evidence_response.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["RuleResultCreateEvidenceResponse"]
+
+
+class RuleResultCreateEvidenceResponse(BaseModel):
+ id: str
+ """The evidence id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who attached the evidence."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ description: Optional[str] = None
+ """A description of what the evidence shows."""
+
+ name: Optional[str] = None
+ """The evidence name."""
+
+ storage_uri: Optional[str] = FieldInfo(alias="storageUri", default=None)
+ """Where the uploaded file is stored.
+
+ Set when the rule's `evidenceType` is `document`.
+ """
+
+ text: Optional[str] = None
+ """The evidence text. Set when the rule's `evidenceType` is `text`."""
+
+ url: Optional[str] = None
+ """A link to the evidence. Set when the rule's `evidenceType` is `url`."""
diff --git a/src/openlayer/types/governance/rule_result_list_evidence_params.py b/src/openlayer/types/governance/rule_result_list_evidence_params.py
new file mode 100644
index 00000000..cef9b326
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_list_evidence_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleResultListEvidenceParams"]
+
+
+class RuleResultListEvidenceParams(TypedDict, total=False):
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
diff --git a/src/openlayer/types/governance/rule_result_list_evidence_response.py b/src/openlayer/types/governance/rule_result_list_evidence_response.py
new file mode 100644
index 00000000..6e45b19e
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_list_evidence_response.py
@@ -0,0 +1,46 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["RuleResultListEvidenceResponse", "Item"]
+
+
+class Item(BaseModel):
+ id: str
+ """The evidence id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who attached the evidence."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ description: Optional[str] = None
+ """A description of what the evidence shows."""
+
+ name: Optional[str] = None
+ """The evidence name."""
+
+ storage_uri: Optional[str] = FieldInfo(alias="storageUri", default=None)
+ """Where the uploaded file is stored.
+
+ Set when the rule's `evidenceType` is `document`.
+ """
+
+ text: Optional[str] = None
+ """The evidence text. Set when the rule's `evidenceType` is `text`."""
+
+ url: Optional[str] = None
+ """A link to the evidence. Set when the rule's `evidenceType` is `url`."""
+
+
+class RuleResultListEvidenceResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/rule_result_list_params.py b/src/openlayer/types/governance/rule_result_list_params.py
new file mode 100644
index 00000000..7ddf514e
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_list_params.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleResultListParams"]
+
+
+class RuleResultListParams(TypedDict, total=False):
+ enabled_framework_only: Annotated[bool, PropertyInfo(alias="enabledFrameworkOnly")]
+ """Only include items belonging to at least one enabled framework."""
+
+ framework_id: Annotated[str, PropertyInfo(alias="frameworkId")]
+ """Only include items belonging to this framework."""
+
+ include_unframed: Annotated[bool, PropertyInfo(alias="includeUnframed")]
+ """Whether to include rules that are not part of any framework."""
+
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
+
+ project_id: Annotated[str, PropertyInfo(alias="projectId")]
+ """Only include items that apply to this project."""
+
+ rule_id: Annotated[str, PropertyInfo(alias="ruleId")]
+ """Only include results of this rule."""
+
+ scope: Literal["project", "workspace"]
+ """Only include rules with this scope."""
+
+ search_query: Annotated[str, PropertyInfo(alias="searchQuery")]
+ """Filter by a free-text search over names and descriptions."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """Only include items whose rule result has this compliance status."""
+
+ type: Literal["platform", "evidence"]
+ """Only include rules of this type."""
diff --git a/src/openlayer/types/governance/rule_result_list_response.py b/src/openlayer/types/governance/rule_result_list_response.py
new file mode 100644
index 00000000..639e7b66
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_list_response.py
@@ -0,0 +1,82 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["RuleResultListResponse", "Item", "ItemBlockedBy", "ItemBlocking"]
+
+
+class ItemBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ItemBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class Item(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ItemBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ItemBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class RuleResultListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/rule_result_retrieve_response.py b/src/openlayer/types/governance/rule_result_retrieve_response.py
new file mode 100644
index 00000000..b7f4db18
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_retrieve_response.py
@@ -0,0 +1,78 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["RuleResultRetrieveResponse", "BlockedBy", "Blocking"]
+
+
+class BlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class Blocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class RuleResultRetrieveResponse(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[BlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[Blocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
diff --git a/src/openlayer/types/governance/rule_result_update_params.py b/src/openlayer/types/governance/rule_result_update_params.py
new file mode 100644
index 00000000..6c6ed1a7
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_update_params.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleResultUpdateParams", "BlockedBy", "Blocking"]
+
+
+class RuleResultUpdateParams(TypedDict, total=False):
+ assignee_id: Annotated[Optional[str], PropertyInfo(alias="assigneeId")]
+ """The user responsible for this result."""
+
+ blocked_by: Annotated[Iterable[BlockedBy], PropertyInfo(alias="blockedBy")]
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Iterable[Blocking]
+ """Rule results that this one blocks."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ deactivated_reason: Annotated[Optional[str], PropertyInfo(alias="deactivatedReason")]
+ """Why the result was excluded."""
+
+
+class BlockedBy(TypedDict, total=False):
+ id: str
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+
+class Blocking(TypedDict, total=False):
+ id: str
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
diff --git a/src/openlayer/types/governance/rule_result_update_response.py b/src/openlayer/types/governance/rule_result_update_response.py
new file mode 100644
index 00000000..299af399
--- /dev/null
+++ b/src/openlayer/types/governance/rule_result_update_response.py
@@ -0,0 +1,78 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["RuleResultUpdateResponse", "BlockedBy", "Blocking"]
+
+
+class BlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class Blocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class RuleResultUpdateResponse(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[BlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[Blocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
diff --git a/src/openlayer/types/governance/rule_retrieve_response.py b/src/openlayer/types/governance/rule_retrieve_response.py
new file mode 100644
index 00000000..055abe65
--- /dev/null
+++ b/src/openlayer/types/governance/rule_retrieve_response.py
@@ -0,0 +1,237 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RuleRetrieveResponse",
+ "Framework",
+ "FrameworkAvatar",
+ "Result",
+ "ResultBlockedBy",
+ "ResultBlocking",
+ "ResultsSummary",
+ "Tag",
+]
+
+
+class FrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class Framework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[FrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class Result(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class Tag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class RuleRetrieveResponse(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[Framework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[Result]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[Tag]] = None
+ """The rule tags associated with the rule."""
diff --git a/src/openlayer/types/governance/rule_stat_retrieve_params.py b/src/openlayer/types/governance/rule_stat_retrieve_params.py
new file mode 100644
index 00000000..b817c194
--- /dev/null
+++ b/src/openlayer/types/governance/rule_stat_retrieve_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleStatRetrieveParams"]
+
+
+class RuleStatRetrieveParams(TypedDict, total=False):
+ framework_id: Annotated[str, PropertyInfo(alias="frameworkId")]
+ """Only include items belonging to this framework."""
+
+ project_id: Annotated[str, PropertyInfo(alias="projectId")]
+ """Only include items that apply to this project."""
diff --git a/src/openlayer/types/governance/rule_stat_retrieve_response.py b/src/openlayer/types/governance/rule_stat_retrieve_response.py
new file mode 100644
index 00000000..84ab85d6
--- /dev/null
+++ b/src/openlayer/types/governance/rule_stat_retrieve_response.py
@@ -0,0 +1,211 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RuleStatRetrieveResponse",
+ "RuleResults",
+ "RuleResultsByRuleScope",
+ "RuleResultsByRuleScopeProject",
+ "RuleResultsByRuleScopeWorkspace",
+ "RuleResultsByRuleType",
+ "RuleResultsByRuleTypeEvidence",
+ "RuleResultsByRuleTypePlatform",
+ "Rules",
+ "RulesByScope",
+ "RulesByType",
+]
+
+
+class RuleResultsByRuleScopeProject(BaseModel):
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleResultsByRuleScopeWorkspace(BaseModel):
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleResultsByRuleScope(BaseModel):
+ project: Optional[RuleResultsByRuleScopeProject] = None
+
+ workspace: Optional[RuleResultsByRuleScopeWorkspace] = None
+
+
+class RuleResultsByRuleTypeEvidence(BaseModel):
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleResultsByRuleTypePlatform(BaseModel):
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+
+class RuleResultsByRuleType(BaseModel):
+ evidence: Optional[RuleResultsByRuleTypeEvidence] = None
+
+ platform: Optional[RuleResultsByRuleTypePlatform] = None
+
+
+class RuleResults(BaseModel):
+ """
+ Counts of rule results, after any filters in the request, with breakdowns by the type and scope of the rule each result belongs to.
+ """
+
+ total: int
+ """The total number of rule results."""
+
+ total_due_soon: int = FieldInfo(alias="totalDueSoon")
+ """The number of rule results whose evidence is about to expire."""
+
+ total_error: int = FieldInfo(alias="totalError")
+ """The number of rule results that errored during evaluation."""
+
+ total_failing: int = FieldInfo(alias="totalFailing")
+ """The number of failing rule results."""
+
+ total_passing: int = FieldInfo(alias="totalPassing")
+ """The number of passing rule results."""
+
+ total_pending: int = FieldInfo(alias="totalPending")
+ """The number of rule results that have not been satisfied yet."""
+
+ total_running: int = FieldInfo(alias="totalRunning")
+ """The number of rule results currently being evaluated."""
+
+ total_skipped: int = FieldInfo(alias="totalSkipped")
+ """The number of skipped rule results."""
+
+ by_rule_scope: Optional[RuleResultsByRuleScope] = FieldInfo(alias="byRuleScope", default=None)
+
+ by_rule_type: Optional[RuleResultsByRuleType] = FieldInfo(alias="byRuleType", default=None)
+
+
+class RulesByScope(BaseModel):
+ """Rule counts by scope."""
+
+ project: Optional[int] = None
+
+ workspace: Optional[int] = None
+
+
+class RulesByType(BaseModel):
+ """Rule counts by type."""
+
+ evidence: Optional[int] = None
+
+ platform: Optional[int] = None
+
+
+class Rules(BaseModel):
+ """Counts of the rules themselves, after any filters in the request."""
+
+ by_scope: Optional[RulesByScope] = FieldInfo(alias="byScope", default=None)
+ """Rule counts by scope."""
+
+ by_type: Optional[RulesByType] = FieldInfo(alias="byType", default=None)
+ """Rule counts by type."""
+
+ total: Optional[int] = None
+ """The total number of rules."""
+
+
+class RuleStatRetrieveResponse(BaseModel):
+ rule_results: RuleResults = FieldInfo(alias="ruleResults")
+ """
+ Counts of rule results, after any filters in the request, with breakdowns by the
+ type and scope of the rule each result belongs to.
+ """
+
+ rules: Rules
+ """Counts of the rules themselves, after any filters in the request."""
diff --git a/src/openlayer/types/governance/rule_tag_list_params.py b/src/openlayer/types/governance/rule_tag_list_params.py
new file mode 100644
index 00000000..aecc9e77
--- /dev/null
+++ b/src/openlayer/types/governance/rule_tag_list_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleTagListParams"]
+
+
+class RuleTagListParams(TypedDict, total=False):
+ page: int
+ """The page to return in a paginated query."""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Maximum number of items to return per page."""
diff --git a/src/openlayer/types/governance/rule_tag_list_response.py b/src/openlayer/types/governance/rule_tag_list_response.py
new file mode 100644
index 00000000..12b53d39
--- /dev/null
+++ b/src/openlayer/types/governance/rule_tag_list_response.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["RuleTagListResponse", "Item"]
+
+
+class Item(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class RuleTagListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/openlayer/types/governance/rule_update_params.py b/src/openlayer/types/governance/rule_update_params.py
new file mode 100644
index 00000000..f8cf2ed3
--- /dev/null
+++ b/src/openlayer/types/governance/rule_update_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["RuleUpdateParams"]
+
+
+class RuleUpdateParams(TypedDict, total=False):
+ assignee_id: Annotated[Optional[str], PropertyInfo(alias="assigneeId")]
+ """The user responsible for satisfying the rule."""
+
+ deactivated: bool
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str]
+ """What the rule requires."""
+
+ name: str
+ """The rule name."""
+
+ renewal_cadence_days: Annotated[Optional[int], PropertyInfo(alias="renewalCadenceDays")]
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ tag_ids: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="tagIds")]
+ """The ids of the rule tags to associate with the rule.
+
+ Replaces the rule's tags. Read them back from `tags`, and list the tags
+ available in the workspace with `GET /workspaces/{workspaceId}/rule-tags`.
+ """
diff --git a/src/openlayer/types/governance/rule_update_response.py b/src/openlayer/types/governance/rule_update_response.py
new file mode 100644
index 00000000..275ba842
--- /dev/null
+++ b/src/openlayer/types/governance/rule_update_response.py
@@ -0,0 +1,237 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RuleUpdateResponse",
+ "Framework",
+ "FrameworkAvatar",
+ "Result",
+ "ResultBlockedBy",
+ "ResultBlocking",
+ "ResultsSummary",
+ "Tag",
+]
+
+
+class FrameworkAvatar(BaseModel):
+ """The icon shown for the framework."""
+
+ type: Literal["emoji", "imageUrl", "builtinImage"]
+
+ value: str
+
+
+class Framework(BaseModel):
+ id: str
+ """The framework id."""
+
+ avatar: Optional[FrameworkAvatar] = None
+ """The icon shown for the framework."""
+
+ built_in_slug: Optional[str] = FieldInfo(alias="builtInSlug", default=None)
+ """
+ Identifies a framework that ships with Openlayer, for example `eu_ai_act`,
+ `iso_42001`, `nist_ai_rmf`, or `traiga`. `null` for frameworks you create
+ yourself.
+ """
+
+ enabled: bool
+ """Whether the framework is active.
+
+ Rules of a disabled framework are not evaluated and do not count towards
+ compliance.
+ """
+
+ name: str
+ """The framework name."""
+
+
+class ResultBlockedBy(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class ResultBlocking(BaseModel):
+ id: Optional[str] = None
+
+ status: Optional[Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]] = None
+ """The compliance status of the rule for this entity."""
+
+
+class Result(BaseModel):
+ id: str
+ """The rule result id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ deactivated: bool
+ """Whether this result is excluded from compliance calculations."""
+
+ rule_id: str = FieldInfo(alias="ruleId")
+ """The rule this result belongs to."""
+
+ status: Literal["running", "passing", "failing", "skipped", "error", "pending", "due_soon"]
+ """The compliance status of the rule for this entity."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule result belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for this result."""
+
+ blocked_by: Optional[List[ResultBlockedBy]] = FieldInfo(alias="blockedBy", default=None)
+ """Rule results that must pass before this one can be satisfied."""
+
+ blocking: Optional[List[ResultBlocking]] = None
+ """Rule results that this one blocks."""
+
+ date_last_evaluated: Optional[datetime] = FieldInfo(alias="dateLastEvaluated", default=None)
+ """When the rule was last evaluated. Platform rules only."""
+
+ date_of_latest_evidence: Optional[datetime] = FieldInfo(alias="dateOfLatestEvidence", default=None)
+ """When the most recent piece of evidence was attached. Evidence rules only."""
+
+ date_of_next_evaluation: Optional[datetime] = FieldInfo(alias="dateOfNextEvaluation", default=None)
+ """When the rule will next be evaluated. Platform rules only."""
+
+ date_of_renewal: Optional[datetime] = FieldInfo(alias="dateOfRenewal", default=None)
+ """When the evidence must be renewed. Evidence rules with a renewal cadence only."""
+
+ deactivated_reason: Optional[str] = FieldInfo(alias="deactivatedReason", default=None)
+ """Why the result was excluded."""
+
+ project_id: Optional[str] = FieldInfo(alias="projectId", default=None)
+ """The project this result was evaluated for. `null` for workspace-scoped rules."""
+
+ status_message: Optional[str] = FieldInfo(alias="statusMessage", default=None)
+ """A human-readable explanation of the status."""
+
+
+class ResultsSummary(BaseModel):
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status filter applied to the request.
+ """
+
+ passing: Optional[int] = None
+
+ total: Optional[int] = None
+
+
+class Tag(BaseModel):
+ id: str
+ """The rule tag id."""
+
+ creator_id: Optional[str] = FieldInfo(alias="creatorId", default=None)
+ """The user who created the tag. `null` for tags that ship with Openlayer."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ immutable: bool
+ """Whether the tag is managed by Openlayer and cannot be edited or deleted."""
+
+ name: str
+ """The tag name."""
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the tag belongs to."""
+
+ color: Optional[str] = None
+ """The color the tag is displayed with."""
+
+
+class RuleUpdateResponse(BaseModel):
+ id: str
+ """The rule id."""
+
+ date_created: datetime = FieldInfo(alias="dateCreated")
+ """The creation date."""
+
+ date_updated: datetime = FieldInfo(alias="dateUpdated")
+ """The last update date."""
+
+ name: str
+ """The rule name."""
+
+ scope: Literal["project", "workspace"]
+ """
+ Whether the rule is evaluated once for the whole workspace, or once per project
+ the rule's frameworks apply to.
+ """
+
+ type: Literal["platform", "evidence"]
+ """
+ `platform` rules are evaluated automatically from the state of your Openlayer
+ workspace. `evidence` rules are satisfied by attaching evidence.
+ """
+
+ workspace_id: str = FieldInfo(alias="workspaceId")
+ """The id of the workspace the rule belongs to."""
+
+ assignee_id: Optional[str] = FieldInfo(alias="assigneeId", default=None)
+ """The user responsible for satisfying the rule."""
+
+ automation_params: Optional[Dict[str, object]] = FieldInfo(alias="automationParams", default=None)
+ """Configuration for the platform check, when the automation takes parameters."""
+
+ automation_type: Optional[str] = FieldInfo(alias="automationType", default=None)
+ """
+ Which workspace signal a platform rule checks, for example
+ `monitoring_mode_enabled`, `test_setup`, or `project_owner_set`. `null` for
+ evidence rules.
+ """
+
+ deactivated: Optional[bool] = None
+ """Whether the rule is excluded from compliance calculations."""
+
+ description: Optional[str] = None
+ """What the rule requires."""
+
+ evidence_type: Optional[Literal["document", "text", "url", "categoryValue"]] = FieldInfo(
+ alias="evidenceType", default=None
+ )
+ """The kind of evidence that satisfies the rule. `null` for platform rules."""
+
+ frameworks: Optional[List[Framework]] = None
+ """The frameworks that include this rule."""
+
+ immutable: Optional[bool] = None
+ """Whether the rule is managed by Openlayer and cannot be edited."""
+
+ renewal_cadence_days: Optional[int] = FieldInfo(alias="renewalCadenceDays", default=None)
+ """How often evidence must be renewed, in days.
+
+ Once evidence is older than this, the rule result becomes `due_soon` and then
+ `failing`.
+ """
+
+ results: Optional[List[Result]] = None
+ """The rule's results, one per entity the rule is evaluated against.
+
+ Only returned when `includeResults` is `true`.
+ """
+
+ results_summary: Optional[ResultsSummary] = FieldInfo(alias="resultsSummary", default=None)
+ """
+ Pass-rate counts across all of the rule's entities, independent of any status
+ filter applied to the request.
+ """
+
+ tags: Optional[List[Tag]] = None
+ """The rule tags associated with the rule."""
diff --git a/src/openlayer/types/inference_pipeline_retrieve_response.py b/src/openlayer/types/inference_pipeline_retrieve_response.py
index d91c59b9..3829955e 100644
--- a/src/openlayer/types/inference_pipeline_retrieve_response.py
+++ b/src/openlayer/types/inference_pipeline_retrieve_response.py
@@ -259,7 +259,7 @@ class Project(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/inference_pipeline_update_response.py b/src/openlayer/types/inference_pipeline_update_response.py
index 49c10a6e..cc6721ae 100644
--- a/src/openlayer/types/inference_pipeline_update_response.py
+++ b/src/openlayer/types/inference_pipeline_update_response.py
@@ -259,7 +259,7 @@ class Project(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/project_create_response.py b/src/openlayer/types/project_create_response.py
index ee049acf..9801ef99 100644
--- a/src/openlayer/types/project_create_response.py
+++ b/src/openlayer/types/project_create_response.py
@@ -74,7 +74,7 @@ class ProjectCreateResponse(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/project_list_response.py b/src/openlayer/types/project_list_response.py
index 1461ddae..8e19432c 100644
--- a/src/openlayer/types/project_list_response.py
+++ b/src/openlayer/types/project_list_response.py
@@ -74,7 +74,7 @@ class Item(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/project_update_response.py b/src/openlayer/types/project_update_response.py
index 26d804e3..c9d24101 100644
--- a/src/openlayer/types/project_update_response.py
+++ b/src/openlayer/types/project_update_response.py
@@ -74,7 +74,7 @@ class ProjectUpdateResponse(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/projects/inference_pipeline_create_response.py b/src/openlayer/types/projects/inference_pipeline_create_response.py
index 55ebce2c..a41ead18 100644
--- a/src/openlayer/types/projects/inference_pipeline_create_response.py
+++ b/src/openlayer/types/projects/inference_pipeline_create_response.py
@@ -259,7 +259,7 @@ class Project(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/projects/inference_pipeline_list_response.py b/src/openlayer/types/projects/inference_pipeline_list_response.py
index 96e12347..b77606bd 100644
--- a/src/openlayer/types/projects/inference_pipeline_list_response.py
+++ b/src/openlayer/types/projects/inference_pipeline_list_response.py
@@ -260,7 +260,7 @@ class ItemProject(BaseModel):
name: str
"""The project name."""
- source: Optional[Literal["web", "api", "null"]] = None
+ source: Optional[Literal["web", "api"]] = None
"""The source of the project."""
task_type: Literal["llm-base", "tabular-classification", "tabular-regression", "text-classification"] = FieldInfo(
diff --git a/src/openlayer/types/storage/__init__.py b/src/openlayer/types/storage/__init__.py
index 1e6151a5..6638e0a5 100644
--- a/src/openlayer/types/storage/__init__.py
+++ b/src/openlayer/types/storage/__init__.py
@@ -4,3 +4,5 @@
from .presigned_url_create_params import PresignedURLCreateParams as PresignedURLCreateParams
from .presigned_url_create_response import PresignedURLCreateResponse as PresignedURLCreateResponse
+from .presigned_url_retrieve_params import PresignedURLRetrieveParams as PresignedURLRetrieveParams
+from .presigned_url_retrieve_response import PresignedURLRetrieveResponse as PresignedURLRetrieveResponse
diff --git a/src/openlayer/types/storage/presigned_url_retrieve_params.py b/src/openlayer/types/storage/presigned_url_retrieve_params.py
new file mode 100644
index 00000000..6868b697
--- /dev/null
+++ b/src/openlayer/types/storage/presigned_url_retrieve_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["PresignedURLRetrieveParams"]
+
+
+class PresignedURLRetrieveParams(TypedDict, total=False):
+ storage_uri: Required[Annotated[str, PropertyInfo(alias="storageUri")]]
+ """The object's storage uri."""
diff --git a/src/openlayer/types/storage/presigned_url_retrieve_response.py b/src/openlayer/types/storage/presigned_url_retrieve_response.py
new file mode 100644
index 00000000..b9579e80
--- /dev/null
+++ b/src/openlayer/types/storage/presigned_url_retrieve_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["PresignedURLRetrieveResponse"]
+
+
+class PresignedURLRetrieveResponse(BaseModel):
+ url: str
+ """The presigned url. Short-lived -- download promptly."""
diff --git a/tests/api_resources/governance/__init__.py b/tests/api_resources/governance/__init__.py
new file mode 100644
index 00000000..fd8019a9
--- /dev/null
+++ b/tests/api_resources/governance/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/governance/frameworks/__init__.py b/tests/api_resources/governance/frameworks/__init__.py
new file mode 100644
index 00000000..fd8019a9
--- /dev/null
+++ b/tests/api_resources/governance/frameworks/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/governance/frameworks/test_documents.py b/tests/api_resources/governance/frameworks/test_documents.py
new file mode 100644
index 00000000..9dc3cb6e
--- /dev/null
+++ b/tests/api_resources/governance/frameworks/test_documents.py
@@ -0,0 +1,214 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance.frameworks import DocumentListResponse, DocumentRetrieveResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestDocuments:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ document = client.governance.frameworks.documents.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DocumentRetrieveResponse, document, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.documents.with_raw_response.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = response.parse()
+ assert_matches_type(DocumentRetrieveResponse, document, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.governance.frameworks.documents.with_streaming_response.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = response.parse()
+ assert_matches_type(DocumentRetrieveResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.documents.with_raw_response.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_id` but received ''"):
+ client.governance.frameworks.documents.with_raw_response.retrieve(
+ document_id="",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Openlayer) -> None:
+ document = client.governance.frameworks.documents.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Openlayer) -> None:
+ document = client.governance.frameworks.documents.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.documents.with_raw_response.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = response.parse()
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Openlayer) -> None:
+ with client.governance.frameworks.documents.with_streaming_response.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = response.parse()
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.documents.with_raw_response.list(
+ framework_id="",
+ )
+
+
+class TestAsyncDocuments:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ document = await async_client.governance.frameworks.documents.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DocumentRetrieveResponse, document, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.documents.with_raw_response.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = await response.parse()
+ assert_matches_type(DocumentRetrieveResponse, document, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.documents.with_streaming_response.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = await response.parse()
+ assert_matches_type(DocumentRetrieveResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.documents.with_raw_response.retrieve(
+ document_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_id` but received ''"):
+ await async_client.governance.frameworks.documents.with_raw_response.retrieve(
+ document_id="",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenlayer) -> None:
+ document = await async_client.governance.frameworks.documents.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ document = await async_client.governance.frameworks.documents.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.documents.with_raw_response.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = await response.parse()
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.documents.with_streaming_response.list(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = await response.parse()
+ assert_matches_type(DocumentListResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.documents.with_raw_response.list(
+ framework_id="",
+ )
diff --git a/tests/api_resources/governance/frameworks/test_sections.py b/tests/api_resources/governance/frameworks/test_sections.py
new file mode 100644
index 00000000..e9155913
--- /dev/null
+++ b/tests/api_resources/governance/frameworks/test_sections.py
@@ -0,0 +1,148 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance.frameworks import SectionListRulesResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSections:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list_rules(self, client: Openlayer) -> None:
+ section = client.governance.frameworks.sections.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ @parametrize
+ def test_method_list_rules_with_all_params(self, client: Openlayer) -> None:
+ section = client.governance.frameworks.sections.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ include_results=True,
+ include_subsection_rules=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status="passing",
+ )
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_rules(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.sections.with_raw_response.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ section = response.parse()
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_rules(self, client: Openlayer) -> None:
+ with client.governance.frameworks.sections.with_streaming_response.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ section = response.parse()
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_rules(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.sections.with_raw_response.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `section_id` but received ''"):
+ client.governance.frameworks.sections.with_raw_response.list_rules(
+ section_id="",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+
+class TestAsyncSections:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ section = await async_client.governance.frameworks.sections.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ @parametrize
+ async def test_method_list_rules_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ section = await async_client.governance.frameworks.sections.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ include_results=True,
+ include_subsection_rules=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status="passing",
+ )
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.sections.with_raw_response.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ section = await response.parse()
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.sections.with_streaming_response.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ section = await response.parse()
+ assert_matches_type(SectionListRulesResponse, section, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.sections.with_raw_response.list_rules(
+ section_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `section_id` but received ''"):
+ await async_client.governance.frameworks.sections.with_raw_response.list_rules(
+ section_id="",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
diff --git a/tests/api_resources/governance/frameworks/test_subsections.py b/tests/api_resources/governance/frameworks/test_subsections.py
new file mode 100644
index 00000000..e84421af
--- /dev/null
+++ b/tests/api_resources/governance/frameworks/test_subsections.py
@@ -0,0 +1,146 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance.frameworks import SubsectionListRulesResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSubsections:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list_rules(self, client: Openlayer) -> None:
+ subsection = client.governance.frameworks.subsections.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ @parametrize
+ def test_method_list_rules_with_all_params(self, client: Openlayer) -> None:
+ subsection = client.governance.frameworks.subsections.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ include_results=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status="passing",
+ )
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_rules(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.subsections.with_raw_response.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ subsection = response.parse()
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_rules(self, client: Openlayer) -> None:
+ with client.governance.frameworks.subsections.with_streaming_response.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ subsection = response.parse()
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_rules(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.subsections.with_raw_response.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subsection_id` but received ''"):
+ client.governance.frameworks.subsections.with_raw_response.list_rules(
+ subsection_id="",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+
+class TestAsyncSubsections:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ subsection = await async_client.governance.frameworks.subsections.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ @parametrize
+ async def test_method_list_rules_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ subsection = await async_client.governance.frameworks.subsections.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ include_results=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ status="passing",
+ )
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.subsections.with_raw_response.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ subsection = await response.parse()
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.subsections.with_streaming_response.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ subsection = await response.parse()
+ assert_matches_type(SubsectionListRulesResponse, subsection, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.subsections.with_raw_response.list_rules(
+ subsection_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `subsection_id` but received ''"):
+ await async_client.governance.frameworks.subsections.with_raw_response.list_rules(
+ subsection_id="",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
diff --git a/tests/api_resources/governance/test_frameworks.py b/tests/api_resources/governance/test_frameworks.py
new file mode 100644
index 00000000..80c54218
--- /dev/null
+++ b/tests/api_resources/governance/test_frameworks.py
@@ -0,0 +1,851 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance import (
+ FrameworkListResponse,
+ FrameworkCreateResponse,
+ FrameworkExportResponse,
+ FrameworkUpdateResponse,
+ FrameworkRetrieveResponse,
+ FrameworkListRulesResponse,
+ FrameworkListProjectsResponse,
+ FrameworkListProjectRuleStatsResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestFrameworks:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ )
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ description="Requirements for high-risk AI systems under the EU AI Act.",
+ enabled=True,
+ project_selector={
+ "match": [
+ {
+ "property": "riskLevel",
+ "value": ["high", "critical"],
+ "operator": "operator",
+ }
+ ]
+ },
+ tags=["regulation", "eu"],
+ )
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.frameworks.with_raw_response.create(
+ workspace_id="",
+ name="EU AI Act",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkRetrieveResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkRetrieveResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkRetrieveResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ avatar={
+ "type": "emoji",
+ "value": "ðŸ§",
+ },
+ description="Requirements for high-risk AI systems under the EU AI Act.",
+ enabled=True,
+ extended_description={"foo": "bar"},
+ href="href",
+ name="EU AI Act",
+ project_selector={
+ "match": [
+ {
+ "property": "riskLevel",
+ "value": ["high", "critical"],
+ "operator": "operator",
+ }
+ ]
+ },
+ tags=["regulation", "eu"],
+ )
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.with_raw_response.update(
+ framework_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ asc=True,
+ completion_operator="is",
+ completion_value=0,
+ enabled=True,
+ include_rule_stats=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ search_query="searchQuery",
+ sort_column="name",
+ tags=["string"],
+ )
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.frameworks.with_raw_response.list(
+ workspace_id="",
+ )
+
+ @parametrize
+ def test_method_export(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_export_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ )
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_export(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_export(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_export(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.with_raw_response.export(
+ framework_id="",
+ )
+
+ @parametrize
+ def test_method_list_project_rule_stats(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_list_project_rule_stats_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ asc=True,
+ page=1,
+ per_page=1,
+ sort_column="projectName",
+ )
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_project_rule_stats(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_project_rule_stats(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_project_rule_stats(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.with_raw_response.list_project_rule_stats(
+ framework_id="",
+ )
+
+ @parametrize
+ def test_method_list_projects(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_list_projects_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_projects(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_projects(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_projects(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.with_raw_response.list_projects(
+ framework_id="",
+ )
+
+ @parametrize
+ def test_method_list_rules(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ @parametrize
+ def test_method_list_rules_with_all_params(self, client: Openlayer) -> None:
+ framework = client.governance.frameworks.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_rules(self, client: Openlayer) -> None:
+ response = client.governance.frameworks.with_raw_response.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = response.parse()
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_rules(self, client: Openlayer) -> None:
+ with client.governance.frameworks.with_streaming_response.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = response.parse()
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_rules(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ client.governance.frameworks.with_raw_response.list_rules(
+ framework_id="",
+ )
+
+
+class TestAsyncFrameworks:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ )
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ description="Requirements for high-risk AI systems under the EU AI Act.",
+ enabled=True,
+ project_selector={
+ "match": [
+ {
+ "property": "riskLevel",
+ "value": ["high", "critical"],
+ "operator": "operator",
+ }
+ ]
+ },
+ tags=["regulation", "eu"],
+ )
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="EU AI Act",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkCreateResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.create(
+ workspace_id="",
+ name="EU AI Act",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkRetrieveResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkRetrieveResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkRetrieveResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ avatar={
+ "type": "emoji",
+ "value": "ðŸ§",
+ },
+ description="Requirements for high-risk AI systems under the EU AI Act.",
+ enabled=True,
+ extended_description={"foo": "bar"},
+ href="href",
+ name="EU AI Act",
+ project_selector={
+ "match": [
+ {
+ "property": "riskLevel",
+ "value": ["high", "critical"],
+ "operator": "operator",
+ }
+ ]
+ },
+ tags=["regulation", "eu"],
+ )
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.update(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkUpdateResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.update(
+ framework_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ asc=True,
+ completion_operator="is",
+ completion_value=0,
+ enabled=True,
+ include_rule_stats=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ search_query="searchQuery",
+ sort_column="name",
+ tags=["string"],
+ )
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkListResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.list(
+ workspace_id="",
+ )
+
+ @parametrize
+ async def test_method_export(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_export_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ )
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_export(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_export(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.export(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkExportResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_export(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.export(
+ framework_id="",
+ )
+
+ @parametrize
+ async def test_method_list_project_rule_stats(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_list_project_rule_stats_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ asc=True,
+ page=1,
+ per_page=1,
+ sort_column="projectName",
+ )
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_project_rule_stats(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_project_rule_stats(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.list_project_rule_stats(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkListProjectRuleStatsResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_project_rule_stats(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.list_project_rule_stats(
+ framework_id="",
+ )
+
+ @parametrize
+ async def test_method_list_projects(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_list_projects_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_projects(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_projects(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.list_projects(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkListProjectsResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_projects(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.list_projects(
+ framework_id="",
+ )
+
+ @parametrize
+ async def test_method_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_method_list_rules_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ framework = await async_client.governance.frameworks.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.frameworks.with_raw_response.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ framework = await response.parse()
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.frameworks.with_streaming_response.list_rules(
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ framework = await response.parse()
+ assert_matches_type(FrameworkListRulesResponse, framework, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_rules(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `framework_id` but received ''"):
+ await async_client.governance.frameworks.with_raw_response.list_rules(
+ framework_id="",
+ )
diff --git a/tests/api_resources/governance/test_rule_results.py b/tests/api_resources/governance/test_rule_results.py
new file mode 100644
index 00000000..10414236
--- /dev/null
+++ b/tests/api_resources/governance/test_rule_results.py
@@ -0,0 +1,532 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance import (
+ RuleResultListResponse,
+ RuleResultUpdateResponse,
+ RuleResultRetrieveResponse,
+ RuleResultListEvidenceResponse,
+ RuleResultCreateEvidenceResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestRuleResults:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultRetrieveResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.governance.rule_results.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = response.parse()
+ assert_matches_type(RuleResultRetrieveResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.governance.rule_results.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = response.parse()
+ assert_matches_type(RuleResultRetrieveResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ client.governance.rule_results.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ blocked_by=[
+ {
+ "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ "status": "passing",
+ }
+ ],
+ blocking=[
+ {
+ "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ "status": "passing",
+ }
+ ],
+ deactivated=True,
+ deactivated_reason="deactivatedReason",
+ )
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Openlayer) -> None:
+ response = client.governance.rule_results.with_raw_response.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = response.parse()
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Openlayer) -> None:
+ with client.governance.rule_results.with_streaming_response.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = response.parse()
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ client.governance.rule_results.with_raw_response.update(
+ rule_result_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ enabled_framework_only=True,
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ include_unframed=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ scope="project",
+ search_query="searchQuery",
+ status="passing",
+ type="platform",
+ )
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Openlayer) -> None:
+ response = client.governance.rule_results.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = response.parse()
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Openlayer) -> None:
+ with client.governance.rule_results.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = response.parse()
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.rule_results.with_raw_response.list(
+ workspace_id="",
+ )
+
+ @parametrize
+ def test_method_create_evidence(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_method_create_evidence_with_all_params(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ description="description",
+ name="Model risk assessment 2026",
+ storage_uri="s3://openlayer-evidence/evidence.pdf",
+ text="text",
+ url="https://openlayer.com/evidence",
+ )
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_evidence(self, client: Openlayer) -> None:
+ response = client.governance.rule_results.with_raw_response.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = response.parse()
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_evidence(self, client: Openlayer) -> None:
+ with client.governance.rule_results.with_streaming_response.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = response.parse()
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create_evidence(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ client.governance.rule_results.with_raw_response.create_evidence(
+ rule_result_id="",
+ )
+
+ @parametrize
+ def test_method_list_evidence(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_method_list_evidence_with_all_params(self, client: Openlayer) -> None:
+ rule_result = client.governance.rule_results.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_evidence(self, client: Openlayer) -> None:
+ response = client.governance.rule_results.with_raw_response.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = response.parse()
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_evidence(self, client: Openlayer) -> None:
+ with client.governance.rule_results.with_streaming_response.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = response.parse()
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_evidence(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ client.governance.rule_results.with_raw_response.list_evidence(
+ rule_result_id="",
+ )
+
+
+class TestAsyncRuleResults:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultRetrieveResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_results.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultRetrieveResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_results.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultRetrieveResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ await async_client.governance.rule_results.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ blocked_by=[
+ {
+ "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ "status": "passing",
+ }
+ ],
+ blocking=[
+ {
+ "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ "status": "passing",
+ }
+ ],
+ deactivated=True,
+ deactivated_reason="deactivatedReason",
+ )
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_results.with_raw_response.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_results.with_streaming_response.update(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultUpdateResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ await async_client.governance.rule_results.with_raw_response.update(
+ rule_result_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ enabled_framework_only=True,
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ include_unframed=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ scope="project",
+ search_query="searchQuery",
+ status="passing",
+ type="platform",
+ )
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_results.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_results.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultListResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.rule_results.with_raw_response.list(
+ workspace_id="",
+ )
+
+ @parametrize
+ async def test_method_create_evidence(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_method_create_evidence_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ description="description",
+ name="Model risk assessment 2026",
+ storage_uri="s3://openlayer-evidence/evidence.pdf",
+ text="text",
+ url="https://openlayer.com/evidence",
+ )
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_evidence(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_results.with_raw_response.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_evidence(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_results.with_streaming_response.create_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultCreateEvidenceResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create_evidence(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ await async_client.governance.rule_results.with_raw_response.create_evidence(
+ rule_result_id="",
+ )
+
+ @parametrize
+ async def test_method_list_evidence(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_method_list_evidence_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule_result = await async_client.governance.rule_results.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_evidence(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_results.with_raw_response.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_evidence(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_results.with_streaming_response.list_evidence(
+ rule_result_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_result = await response.parse()
+ assert_matches_type(RuleResultListEvidenceResponse, rule_result, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_evidence(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_result_id` but received ''"):
+ await async_client.governance.rule_results.with_raw_response.list_evidence(
+ rule_result_id="",
+ )
diff --git a/tests/api_resources/governance/test_rule_stats.py b/tests/api_resources/governance/test_rule_stats.py
new file mode 100644
index 00000000..6cd74947
--- /dev/null
+++ b/tests/api_resources/governance/test_rule_stats.py
@@ -0,0 +1,118 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance import RuleStatRetrieveResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestRuleStats:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ rule_stat = client.governance.rule_stats.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ @parametrize
+ def test_method_retrieve_with_all_params(self, client: Openlayer) -> None:
+ rule_stat = client.governance.rule_stats.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.governance.rule_stats.with_raw_response.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_stat = response.parse()
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.governance.rule_stats.with_streaming_response.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_stat = response.parse()
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.rule_stats.with_raw_response.retrieve(
+ workspace_id="",
+ )
+
+
+class TestAsyncRuleStats:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ rule_stat = await async_client.governance.rule_stats.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ @parametrize
+ async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule_stat = await async_client.governance.rule_stats.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_stats.with_raw_response.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_stat = await response.parse()
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_stats.with_streaming_response.retrieve(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_stat = await response.parse()
+ assert_matches_type(RuleStatRetrieveResponse, rule_stat, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.rule_stats.with_raw_response.retrieve(
+ workspace_id="",
+ )
diff --git a/tests/api_resources/governance/test_rule_tags.py b/tests/api_resources/governance/test_rule_tags.py
new file mode 100644
index 00000000..349b03f9
--- /dev/null
+++ b/tests/api_resources/governance/test_rule_tags.py
@@ -0,0 +1,118 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance import RuleTagListResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestRuleTags:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: Openlayer) -> None:
+ rule_tag = client.governance.rule_tags.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Openlayer) -> None:
+ rule_tag = client.governance.rule_tags.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Openlayer) -> None:
+ response = client.governance.rule_tags.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_tag = response.parse()
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Openlayer) -> None:
+ with client.governance.rule_tags.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_tag = response.parse()
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.rule_tags.with_raw_response.list(
+ workspace_id="",
+ )
+
+
+class TestAsyncRuleTags:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenlayer) -> None:
+ rule_tag = await async_client.governance.rule_tags.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule_tag = await async_client.governance.rule_tags.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rule_tags.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule_tag = await response.parse()
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rule_tags.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule_tag = await response.parse()
+ assert_matches_type(RuleTagListResponse, rule_tag, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.rule_tags.with_raw_response.list(
+ workspace_id="",
+ )
diff --git a/tests/api_resources/governance/test_rules.py b/tests/api_resources/governance/test_rules.py
new file mode 100644
index 00000000..f8de60e8
--- /dev/null
+++ b/tests/api_resources/governance/test_rules.py
@@ -0,0 +1,543 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types.governance import (
+ RuleListResponse,
+ RuleCreateResponse,
+ RuleUpdateResponse,
+ RuleRetrieveResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestRules:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Openlayer) -> None:
+ rule = client.governance.rules.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ )
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Openlayer) -> None:
+ rule = client.governance.rules.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ automation_params={"foo": "bar"},
+ automation_type="monitoring_mode_enabled",
+ deactivated=True,
+ description="Each project must have Openlayer monitoring mode enabled.",
+ evidence_type="document",
+ renewal_cadence_days=90,
+ tag_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ )
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Openlayer) -> None:
+ response = client.governance.rules.with_raw_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = response.parse()
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Openlayer) -> None:
+ with client.governance.rules.with_streaming_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = response.parse()
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.rules.with_raw_response.create(
+ workspace_id="",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ rule = client.governance.rules.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleRetrieveResponse, rule, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.governance.rules.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = response.parse()
+ assert_matches_type(RuleRetrieveResponse, rule, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.governance.rules.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = response.parse()
+ assert_matches_type(RuleRetrieveResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_id` but received ''"):
+ client.governance.rules.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Openlayer) -> None:
+ rule = client.governance.rules.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Openlayer) -> None:
+ rule = client.governance.rules.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ deactivated=True,
+ description="Each project must have Openlayer monitoring mode enabled.",
+ name="Monitoring enabled",
+ renewal_cadence_days=90,
+ tag_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ )
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Openlayer) -> None:
+ response = client.governance.rules.with_raw_response.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = response.parse()
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Openlayer) -> None:
+ with client.governance.rules.with_streaming_response.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = response.parse()
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_id` but received ''"):
+ client.governance.rules.with_raw_response.update(
+ rule_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Openlayer) -> None:
+ rule = client.governance.rules.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Openlayer) -> None:
+ rule = client.governance.rules.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ asc=True,
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ deactivated=True,
+ enabled_framework_only=True,
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ group="open",
+ include_results=True,
+ include_unframed=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ scope="project",
+ search_query="searchQuery",
+ sort_by="name",
+ status="passing",
+ tags=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ type="platform",
+ )
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Openlayer) -> None:
+ response = client.governance.rules.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = response.parse()
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Openlayer) -> None:
+ with client.governance.rules.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = response.parse()
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ client.governance.rules.with_raw_response.list(
+ workspace_id="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Openlayer) -> None:
+ rule = client.governance.rules.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert rule is None
+
+ @parametrize
+ def test_raw_response_delete(self, client: Openlayer) -> None:
+ response = client.governance.rules.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = response.parse()
+ assert rule is None
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Openlayer) -> None:
+ with client.governance.rules.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = response.parse()
+ assert rule is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_id` but received ''"):
+ client.governance.rules.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncRules:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ )
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ automation_params={"foo": "bar"},
+ automation_type="monitoring_mode_enabled",
+ deactivated=True,
+ description="Each project must have Openlayer monitoring mode enabled.",
+ evidence_type="document",
+ renewal_cadence_days=90,
+ tag_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ )
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rules.with_raw_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = await response.parse()
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rules.with_streaming_response.create(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = await response.parse()
+ assert_matches_type(RuleCreateResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.rules.with_raw_response.create(
+ workspace_id="",
+ name="Monitoring enabled",
+ scope="project",
+ type="platform",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleRetrieveResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rules.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = await response.parse()
+ assert_matches_type(RuleRetrieveResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rules.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = await response.parse()
+ assert_matches_type(RuleRetrieveResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_id` but received ''"):
+ await async_client.governance.rules.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ deactivated=True,
+ description="Each project must have Openlayer monitoring mode enabled.",
+ name="Monitoring enabled",
+ renewal_cadence_days=90,
+ tag_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ )
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rules.with_raw_response.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = await response.parse()
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rules.with_streaming_response.update(
+ rule_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = await response.parse()
+ assert_matches_type(RuleUpdateResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_id` but received ''"):
+ await async_client.governance.rules.with_raw_response.update(
+ rule_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ asc=True,
+ assignee_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ deactivated=True,
+ enabled_framework_only=True,
+ framework_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ group="open",
+ include_results=True,
+ include_unframed=True,
+ page=1,
+ per_page=1,
+ project_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ scope="project",
+ search_query="searchQuery",
+ sort_by="name",
+ status="passing",
+ tags=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ type="platform",
+ )
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rules.with_raw_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = await response.parse()
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rules.with_streaming_response.list(
+ workspace_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = await response.parse()
+ assert_matches_type(RuleListResponse, rule, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"):
+ await async_client.governance.rules.with_raw_response.list(
+ workspace_id="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncOpenlayer) -> None:
+ rule = await async_client.governance.rules.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert rule is None
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.governance.rules.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ rule = await response.parse()
+ assert rule is None
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.governance.rules.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ rule = await response.parse()
+ assert rule is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `rule_id` but received ''"):
+ await async_client.governance.rules.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/storage/test_presigned_url.py b/tests/api_resources/storage/test_presigned_url.py
index 4f2daa29..6ef87cc8 100644
--- a/tests/api_resources/storage/test_presigned_url.py
+++ b/tests/api_resources/storage/test_presigned_url.py
@@ -9,7 +9,10 @@
from openlayer import Openlayer, AsyncOpenlayer
from tests.utils import assert_matches_type
-from openlayer.types.storage import PresignedURLCreateResponse
+from openlayer.types.storage import (
+ PresignedURLCreateResponse,
+ PresignedURLRetrieveResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -48,6 +51,37 @@ def test_streaming_response_create(self, client: Openlayer) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ presigned_url = client.storage.presigned_url.retrieve(
+ storage_uri="storageUri",
+ )
+ assert_matches_type(PresignedURLRetrieveResponse, presigned_url, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.storage.presigned_url.with_raw_response.retrieve(
+ storage_uri="storageUri",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ presigned_url = response.parse()
+ assert_matches_type(PresignedURLRetrieveResponse, presigned_url, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.storage.presigned_url.with_streaming_response.retrieve(
+ storage_uri="storageUri",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ presigned_url = response.parse()
+ assert_matches_type(PresignedURLRetrieveResponse, presigned_url, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
class TestAsyncPresignedURL:
parametrize = pytest.mark.parametrize(
@@ -84,3 +118,34 @@ async def test_streaming_response_create(self, async_client: AsyncOpenlayer) ->
assert_matches_type(PresignedURLCreateResponse, presigned_url, path=["response"])
assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ presigned_url = await async_client.storage.presigned_url.retrieve(
+ storage_uri="storageUri",
+ )
+ assert_matches_type(PresignedURLRetrieveResponse, presigned_url, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.storage.presigned_url.with_raw_response.retrieve(
+ storage_uri="storageUri",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ presigned_url = await response.parse()
+ assert_matches_type(PresignedURLRetrieveResponse, presigned_url, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.storage.presigned_url.with_streaming_response.retrieve(
+ storage_uri="storageUri",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ presigned_url = await response.parse()
+ assert_matches_type(PresignedURLRetrieveResponse, presigned_url, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_background_tasks.py b/tests/api_resources/test_background_tasks.py
new file mode 100644
index 00000000..013fbb58
--- /dev/null
+++ b/tests/api_resources/test_background_tasks.py
@@ -0,0 +1,100 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from openlayer import Openlayer, AsyncOpenlayer
+from tests.utils import assert_matches_type
+from openlayer.types import BackgroundTaskRetrieveResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestBackgroundTasks:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Openlayer) -> None:
+ background_task = client.background_tasks.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(BackgroundTaskRetrieveResponse, background_task, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Openlayer) -> None:
+ response = client.background_tasks.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ background_task = response.parse()
+ assert_matches_type(BackgroundTaskRetrieveResponse, background_task, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Openlayer) -> None:
+ with client.background_tasks.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ background_task = response.parse()
+ assert_matches_type(BackgroundTaskRetrieveResponse, background_task, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Openlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_id` but received ''"):
+ client.background_tasks.with_raw_response.retrieve(
+ "",
+ )
+
+
+class TestAsyncBackgroundTasks:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ background_task = await async_client.background_tasks.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(BackgroundTaskRetrieveResponse, background_task, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ response = await async_client.background_tasks.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ background_task = await response.parse()
+ assert_matches_type(BackgroundTaskRetrieveResponse, background_task, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ async with async_client.background_tasks.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ background_task = await response.parse()
+ assert_matches_type(BackgroundTaskRetrieveResponse, background_task, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenlayer) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_id` but received ''"):
+ await async_client.background_tasks.with_raw_response.retrieve(
+ "",
+ )