From caa47dc5b9d671046dca4dd5378a72018ed5d334 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 9 May 2025 03:00:10 +0000
Subject: [PATCH 01/11] chore: sync repo
---
.github/workflows/create-releases.yml | 38 ++++++
.github/workflows/publish-pypi.yml | 8 +-
.github/workflows/release-doctor.yml | 1 +
bin/check-release-environment | 4 +
.../tracing/openlit/openlit_tracing.ipynb | 125 ------------------
.../openllmetry/openllmetry_tracing.ipynb | 12 +-
6 files changed, 55 insertions(+), 133 deletions(-)
create mode 100644 .github/workflows/create-releases.yml
delete mode 100644 examples/tracing/openlit/openlit_tracing.ipynb
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
new file mode 100644
index 00000000..14e8bdab
--- /dev/null
+++ b/.github/workflows/create-releases.yml
@@ -0,0 +1,38 @@
+name: Create releases
+on:
+ schedule:
+ - cron: '0 5 * * *' # every day at 5am UTC
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+ name: release
+ if: github.ref == 'refs/heads/main' && github.repository == 'openlayer-ai/openlayer-python'
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: stainless-api/trigger-release-please@v1
+ id: release
+ with:
+ repo: ${{ github.event.repository.full_name }}
+ stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}
+
+ - name: Install Rye
+ if: ${{ steps.release.outputs.releases_created }}
+ run: |
+ curl -sSf https://rye.astral.sh/get | bash
+ echo "$HOME/.rye/shims" >> $GITHUB_PATH
+ env:
+ RYE_VERSION: '0.44.0'
+ RYE_INSTALL_OPTION: '--yes'
+
+ - name: Publish to PyPI
+ if: ${{ steps.release.outputs.releases_created }}
+ run: |
+ bash ./bin/publish-pypi
+ env:
+ PYPI_TOKEN: ${{ secrets.OPENLAYER_PYPI_TOKEN || secrets.PYPI_TOKEN }}
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index 3779ab92..362002d6 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -1,13 +1,9 @@
-# This workflow is triggered when a GitHub release is created.
-# It can also be run manually to re-publish to PyPI in case it failed for some reason.
-# You can run this workflow by navigating to https://www.github.com/openlayer-ai/openlayer-python/actions/workflows/publish-pypi.yml
+# workflow for re-running publishing to PyPI in case it fails for some reason
+# you can run this workflow by navigating to https://www.github.com/openlayer-ai/openlayer-python/actions/workflows/publish-pypi.yml
name: Publish PyPI
on:
workflow_dispatch:
- release:
- types: [published]
-
jobs:
publish:
name: publish
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index d6d56f28..95f1a185 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -18,4 +18,5 @@ jobs:
run: |
bash ./bin/check-release-environment
env:
+ STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }}
PYPI_TOKEN: ${{ secrets.OPENLAYER_PYPI_TOKEN || secrets.PYPI_TOKEN }}
diff --git a/bin/check-release-environment b/bin/check-release-environment
index c0077294..b737e128 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -2,6 +2,10 @@
errors=()
+if [ -z "${STAINLESS_API_KEY}" ]; then
+ errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.")
+fi
+
if [ -z "${PYPI_TOKEN}" ]; then
errors+=("The OPENLAYER_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
fi
diff --git a/examples/tracing/openlit/openlit_tracing.ipynb b/examples/tracing/openlit/openlit_tracing.ipynb
deleted file mode 100644
index d43674b4..00000000
--- a/examples/tracing/openlit/openlit_tracing.ipynb
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "2722b419",
- "metadata": {},
- "source": [
- "[](https://colab.research.google.com/github/openlayer-ai/openlayer-python/blob/main/examples/tracing/openlit/openlit_tracing.ipynb)\n",
- "\n",
- "\n",
- "# OpenLIT quickstart\n",
- "\n",
- "This notebook shows how to export traces captured by [OpenLIT](https://docs.openlit.io/latest/features/tracing) to Openlayer. The integration is done via the Openlayer's [OpenTelemetry endpoint](https://www.openlayer.com/docs/integrations/opentelemetry). For more information, refer to the [OpenLIT integration guide](https://www.openlayer.com/docs/integrations/openlit)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "020c8f6a",
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install openai openlit"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "75c2a473",
- "metadata": {},
- "source": [
- "## 1. Set the environment variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "f3f4fa13",
- "metadata": {},
- "outputs": [],
- "source": [
- "import os\n",
- "\n",
- "import openai\n",
- "\n",
- "os.environ[\"OPENAI_API_KEY\"] = \"YOUR_OPENAI_API_KEY_HERE\"\n",
- "\n",
- "os.environ[\"OTEL_EXPORTER_OTLP_ENDPOINT\"] = \"https://api.openlayer.com/v1/otel\"\n",
- "os.environ[\"OTEL_EXPORTER_OTLP_HEADERS\"] = \"Authorization=Bearer YOUR_OPENLAYER_API_KEY_HERE, x-bt-parent=pipeline_id:YOUR_OPENLAYER_PIPELINE_ID_HERE\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9758533f",
- "metadata": {},
- "source": [
- "## 2. Initialize OpenLIT instrumentation"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c35d9860-dc41-4f7c-8d69-cc2ac7e5e485",
- "metadata": {},
- "outputs": [],
- "source": [
- "import openlit\n",
- "\n",
- "openlit.init(disable_batch=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "72a6b954",
- "metadata": {},
- "source": [
- "## 3. Use LLMs and workflows as usual\n",
- "\n",
- "That's it! Now you can continue using LLMs and workflows as usual.The trace data is automatically exported to Openlayer and you can start creating tests around it."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "e00c1c79",
- "metadata": {},
- "outputs": [],
- "source": [
- "client = openai.OpenAI()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "abaf6987-c257-4f0d-96e7-3739b24c7206",
- "metadata": {},
- "outputs": [],
- "source": [
- "client.chat.completions.create(\n",
- " model=\"gpt-4o-mini\", messages=[{\"role\": \"user\", \"content\": \"How are you doing today?\"}]\n",
- ")"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "openlayer-assistant",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.18"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/tracing/openllmetry/openllmetry_tracing.ipynb b/examples/tracing/openllmetry/openllmetry_tracing.ipynb
index bb215775..eb1833ed 100644
--- a/examples/tracing/openllmetry/openllmetry_tracing.ipynb
+++ b/examples/tracing/openllmetry/openllmetry_tracing.ipynb
@@ -10,7 +10,7 @@
"\n",
"# OpenLLMetry quickstart\n",
"\n",
- "This notebook shows how to export traces captured by [OpenLLMetry](https://github.com/traceloop/openllmetry) (by Traceloop) to Openlayer. The integration is done via the Openlayer's [OpenTelemetry endpoint](https://www.openlayer.com/docs/integrations/opentelemetry). For more information, refer to the [OpenLLMetry integration guide](https://www.openlayer.com/docs/integrations/openllmetry)."
+ "This notebook shows how to export traces captured by [OpenLLMetry](https://github.com/traceloop/openllmetry) (by Traceloop) to Openlayer. The integration is done via the Openlayer's [OpenTelemetry endpoint](https://www.openlayer.com/docs/integrations/opentelemetry)."
]
},
{
@@ -62,7 +62,15 @@
"execution_count": null,
"id": "c35d9860-dc41-4f7c-8d69-cc2ac7e5e485",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Failed to export batch code: 404, reason: {\"error\": \"The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.\", \"code\": 404}\n"
+ ]
+ }
+ ],
"source": [
"from traceloop.sdk import Traceloop\n",
"\n",
From f990977209f13f02b1b87ab98bef5eef50414ea9 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 9 May 2025 03:00:37 +0000
Subject: [PATCH 02/11] chore(internal): codegen related update
---
.github/workflows/create-releases.yml | 38 ---------------------------
.github/workflows/publish-pypi.yml | 8 ++++--
.github/workflows/release-doctor.yml | 1 -
bin/check-release-environment | 4 ---
4 files changed, 6 insertions(+), 45 deletions(-)
delete mode 100644 .github/workflows/create-releases.yml
diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
deleted file mode 100644
index 14e8bdab..00000000
--- a/.github/workflows/create-releases.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: Create releases
-on:
- schedule:
- - cron: '0 5 * * *' # every day at 5am UTC
- push:
- branches:
- - main
-
-jobs:
- release:
- name: release
- if: github.ref == 'refs/heads/main' && github.repository == 'openlayer-ai/openlayer-python'
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
-
- - uses: stainless-api/trigger-release-please@v1
- id: release
- with:
- repo: ${{ github.event.repository.full_name }}
- stainless-api-key: ${{ secrets.STAINLESS_API_KEY }}
-
- - name: Install Rye
- if: ${{ steps.release.outputs.releases_created }}
- run: |
- curl -sSf https://rye.astral.sh/get | bash
- echo "$HOME/.rye/shims" >> $GITHUB_PATH
- env:
- RYE_VERSION: '0.44.0'
- RYE_INSTALL_OPTION: '--yes'
-
- - name: Publish to PyPI
- if: ${{ steps.release.outputs.releases_created }}
- run: |
- bash ./bin/publish-pypi
- env:
- PYPI_TOKEN: ${{ secrets.OPENLAYER_PYPI_TOKEN || secrets.PYPI_TOKEN }}
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
index 362002d6..3779ab92 100644
--- a/.github/workflows/publish-pypi.yml
+++ b/.github/workflows/publish-pypi.yml
@@ -1,9 +1,13 @@
-# workflow for re-running publishing to PyPI in case it fails for some reason
-# you can run this workflow by navigating to https://www.github.com/openlayer-ai/openlayer-python/actions/workflows/publish-pypi.yml
+# This workflow is triggered when a GitHub release is created.
+# It can also be run manually to re-publish to PyPI in case it failed for some reason.
+# You can run this workflow by navigating to https://www.github.com/openlayer-ai/openlayer-python/actions/workflows/publish-pypi.yml
name: Publish PyPI
on:
workflow_dispatch:
+ release:
+ types: [published]
+
jobs:
publish:
name: publish
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 95f1a185..d6d56f28 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -18,5 +18,4 @@ jobs:
run: |
bash ./bin/check-release-environment
env:
- STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }}
PYPI_TOKEN: ${{ secrets.OPENLAYER_PYPI_TOKEN || secrets.PYPI_TOKEN }}
diff --git a/bin/check-release-environment b/bin/check-release-environment
index b737e128..c0077294 100644
--- a/bin/check-release-environment
+++ b/bin/check-release-environment
@@ -2,10 +2,6 @@
errors=()
-if [ -z "${STAINLESS_API_KEY}" ]; then
- errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.")
-fi
-
if [ -z "${PYPI_TOKEN}" ]; then
errors+=("The OPENLAYER_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
fi
From 3de384be80ba27ba97a6079a78b75cdeadf55e5f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 9 May 2025 03:01:19 +0000
Subject: [PATCH 03/11] chore(internal): avoid errors for isinstance checks on
proxies
---
src/openlayer/_utils/_proxy.py | 5 ++++-
tests/test_utils/test_proxy.py | 11 +++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/openlayer/_utils/_proxy.py b/src/openlayer/_utils/_proxy.py
index ffd883e9..0f239a33 100644
--- a/src/openlayer/_utils/_proxy.py
+++ b/src/openlayer/_utils/_proxy.py
@@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]:
@property # type: ignore
@override
def __class__(self) -> type: # pyright: ignore
- proxied = self.__get_proxied__()
+ try:
+ proxied = self.__get_proxied__()
+ except Exception:
+ return type(self)
if issubclass(type(proxied), LazyProxy):
return type(proxied)
return proxied.__class__
diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py
index 7f09e39e..da6f4851 100644
--- a/tests/test_utils/test_proxy.py
+++ b/tests/test_utils/test_proxy.py
@@ -21,3 +21,14 @@ def test_recursive_proxy() -> None:
assert dir(proxy) == []
assert type(proxy).__name__ == "RecursiveLazyProxy"
assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy"
+
+
+def test_isinstance_does_not_error() -> None:
+ class AlwaysErrorProxy(LazyProxy[Any]):
+ @override
+ def __load__(self) -> Any:
+ raise RuntimeError("Mocking missing dependency")
+
+ proxy = AlwaysErrorProxy()
+ assert not isinstance(proxy, dict)
+ assert isinstance(proxy, LazyProxy)
From 84077531a8491bc48c8fe5d67a9076a27ba21fce Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 10 May 2025 02:48:45 +0000
Subject: [PATCH 04/11] fix(package): support direct resource imports
---
src/openlayer/__init__.py | 5 +++++
src/openlayer/_utils/_resources_proxy.py | 24 ++++++++++++++++++++++++
2 files changed, 29 insertions(+)
create mode 100644 src/openlayer/_utils/_resources_proxy.py
diff --git a/src/openlayer/__init__.py b/src/openlayer/__init__.py
index e6918d32..8b434e24 100644
--- a/src/openlayer/__init__.py
+++ b/src/openlayer/__init__.py
@@ -1,5 +1,7 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+import typing as _t
+
from . import types
from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
from ._utils import file_from_path
@@ -78,6 +80,9 @@
"DefaultAsyncHttpxClient",
]
+if not _t.TYPE_CHECKING:
+ from ._utils._resources_proxy import resources as resources
+
_setup_logging()
# Update the __module__ attribute for exported symbols so that
diff --git a/src/openlayer/_utils/_resources_proxy.py b/src/openlayer/_utils/_resources_proxy.py
new file mode 100644
index 00000000..d1c684e5
--- /dev/null
+++ b/src/openlayer/_utils/_resources_proxy.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+from typing import Any
+from typing_extensions import override
+
+from ._proxy import LazyProxy
+
+
+class ResourcesProxy(LazyProxy[Any]):
+ """A proxy for the `openlayer.resources` module.
+
+ This is used so that we can lazily import `openlayer.resources` only when
+ needed *and* so that users can just import `openlayer` and reference `openlayer.resources`
+ """
+
+ @override
+ def __load__(self) -> Any:
+ import importlib
+
+ mod = importlib.import_module("openlayer.resources")
+ return mod
+
+
+resources = ResourcesProxy().__as_proxied__()
From 0aadb0a4deed48d46981fd44b308fba5bbc5a3c1 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 15 May 2025 03:24:46 +0000
Subject: [PATCH 05/11] chore(ci): upload sdks to package manager
---
.github/workflows/ci.yml | 24 ++++++++++++++++++++++++
scripts/utils/upload-artifact.sh | 25 +++++++++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100755 scripts/utils/upload-artifact.sh
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 009b7821..ac8eac82 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,3 +29,27 @@ jobs:
- name: Run lints
run: ./scripts/lint
+
+ upload:
+ if: github.repository == 'stainless-sdks/openlayer-python'
+ timeout-minutes: 10
+ name: upload
+ permissions:
+ contents: read
+ id-token: write
+ runs-on: depot-ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Get GitHub OIDC Token
+ id: github-oidc
+ uses: actions/github-script@v6
+ with:
+ script: core.setOutput('github_token', await core.getIDToken());
+
+ - name: Upload tarball
+ env:
+ URL: https://pkg.stainless.com/s
+ AUTH: ${{ steps.github-oidc.outputs.github_token }}
+ SHA: ${{ github.sha }}
+ run: ./scripts/utils/upload-artifact.sh
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
new file mode 100755
index 00000000..35b89008
--- /dev/null
+++ b/scripts/utils/upload-artifact.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -exuo pipefail
+
+RESPONSE=$(curl -X POST "$URL" \
+ -H "Authorization: Bearer $AUTH" \
+ -H "Content-Type: application/json")
+
+SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url')
+
+if [[ "$SIGNED_URL" == "null" ]]; then
+ echo -e "\033[31mFailed to get signed URL.\033[0m"
+ exit 1
+fi
+
+UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \
+ -H "Content-Type: application/gzip" \
+ --data-binary @- "$SIGNED_URL" 2>&1)
+
+if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
+ echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
+ echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/openlayer-python/$SHA'\033[0m"
+else
+ echo -e "\033[31mFailed to upload artifact.\033[0m"
+ exit 1
+fi
From d7d4fd2e5464f87660a30edd1067aef930b2249a Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 16 May 2025 02:54:16 +0000
Subject: [PATCH 06/11] chore(ci): fix installation instructions
---
scripts/utils/upload-artifact.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
index 35b89008..ac8f23db 100755
--- a/scripts/utils/upload-artifact.sh
+++ b/scripts/utils/upload-artifact.sh
@@ -18,7 +18,7 @@ UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \
if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
- echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/openlayer-python/$SHA'\033[0m"
+ echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/openlayer-python/$SHA'\033[0m"
else
echo -e "\033[31mFailed to upload artifact.\033[0m"
exit 1
From 120114ad9d40ce7c41112522f2951dd92be61eaf Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 17 May 2025 02:51:48 +0000
Subject: [PATCH 07/11] chore(internal): codegen related update
---
scripts/utils/upload-artifact.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
index ac8f23db..e7a0c9ec 100755
--- a/scripts/utils/upload-artifact.sh
+++ b/scripts/utils/upload-artifact.sh
@@ -18,7 +18,7 @@ UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \
if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
- echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/openlayer-python/$SHA'\033[0m"
+ echo -e "\033[32mInstallation: pip install --pre 'https://pkg.stainless.com/s/openlayer-python/$SHA'\033[0m"
else
echo -e "\033[31mFailed to upload artifact.\033[0m"
exit 1
From 27794bc2ff2f34c10c1635fcf14677e0711a8af0 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 22 May 2025 02:31:17 +0000
Subject: [PATCH 08/11] chore(docs): grammar improvements
---
SECURITY.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/SECURITY.md b/SECURITY.md
index 8614b059..dc108d01 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -16,11 +16,11 @@ before making any information public.
## Reporting Non-SDK Related Security Issues
If you encounter security issues that are not directly related to SDKs but pertain to the services
-or products provided by Openlayer please follow the respective company's security reporting guidelines.
+or products provided by Openlayer, please follow the respective company's security reporting guidelines.
### Openlayer Terms and Policies
-Please contact support@openlayer.com for any questions or concerns regarding security of our services.
+Please contact support@openlayer.com for any questions or concerns regarding the security of our services.
---
From 9f8db4a42a79af923d55ec636e43bf49ce80bc50 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 3 Jun 2025 02:22:54 +0000
Subject: [PATCH 09/11] chore(docs): remove reference to rye shell
---
CONTRIBUTING.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1a053ce9..da31df73 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,8 +17,7 @@ $ rye sync --all-features
You can then run scripts using `rye run python script.py` or by activating the virtual environment:
```sh
-$ rye shell
-# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work
+# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work
$ source .venv/bin/activate
# now you can omit the `rye run` prefix
From 87d89863dd9c4f700b8a8910ce14d2a961404336 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 3 Jun 2025 03:36:56 +0000
Subject: [PATCH 10/11] feat(client): add follow_redirects request option
---
src/openlayer/_base_client.py | 6 ++++
src/openlayer/_models.py | 2 ++
src/openlayer/_types.py | 2 ++
tests/test_client.py | 54 +++++++++++++++++++++++++++++++++++
4 files changed, 64 insertions(+)
diff --git a/src/openlayer/_base_client.py b/src/openlayer/_base_client.py
index df1dab62..718469f7 100644
--- a/src/openlayer/_base_client.py
+++ b/src/openlayer/_base_client.py
@@ -960,6 +960,9 @@ def request(
if self.custom_auth is not None:
kwargs["auth"] = self.custom_auth
+ if options.follow_redirects is not None:
+ kwargs["follow_redirects"] = options.follow_redirects
+
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
response = None
@@ -1460,6 +1463,9 @@ async def request(
if self.custom_auth is not None:
kwargs["auth"] = self.custom_auth
+ if options.follow_redirects is not None:
+ kwargs["follow_redirects"] = options.follow_redirects
+
log.debug("Sending HTTP Request: %s %s", request.method, request.url)
response = None
diff --git a/src/openlayer/_models.py b/src/openlayer/_models.py
index 798956f1..4f214980 100644
--- a/src/openlayer/_models.py
+++ b/src/openlayer/_models.py
@@ -737,6 +737,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
idempotency_key: str
json_data: Body
extra_json: AnyMapping
+ follow_redirects: bool
@final
@@ -750,6 +751,7 @@ class FinalRequestOptions(pydantic.BaseModel):
files: Union[HttpxRequestFiles, None] = None
idempotency_key: Union[str, None] = None
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
+ follow_redirects: Union[bool, None] = None
# It should be noted that we cannot use `json` here as that would override
# a BaseModel method in an incompatible fashion.
diff --git a/src/openlayer/_types.py b/src/openlayer/_types.py
index c19dc25f..75357538 100644
--- a/src/openlayer/_types.py
+++ b/src/openlayer/_types.py
@@ -100,6 +100,7 @@ class RequestOptions(TypedDict, total=False):
params: Query
extra_json: AnyMapping
idempotency_key: str
+ follow_redirects: bool
# Sentinel class used until PEP 0661 is accepted
@@ -215,3 +216,4 @@ class _GenericAlias(Protocol):
class HttpxSendArgs(TypedDict, total=False):
auth: httpx.Auth
+ follow_redirects: bool
diff --git a/tests/test_client.py b/tests/test_client.py
index 265760da..7562a048 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -921,6 +921,33 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
+ @pytest.mark.respx(base_url=base_url)
+ def test_follow_redirects(self, respx_mock: MockRouter) -> None:
+ # Test that the default follow_redirects=True allows following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+ respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+
+ response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ assert response.status_code == 200
+ assert response.json() == {"status": "ok"}
+
+ @pytest.mark.respx(base_url=base_url)
+ def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
+ # Test that follow_redirects=False prevents following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+
+ with pytest.raises(APIStatusError) as exc_info:
+ self.client.post(
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
+ )
+
+ assert exc_info.value.response.status_code == 302
+ assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
+
class TestAsyncOpenlayer:
client = AsyncOpenlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True)
@@ -1847,3 +1874,30 @@ async def test_main() -> None:
raise AssertionError("calling get_platform using asyncify resulted in a hung process")
time.sleep(0.1)
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_follow_redirects(self, respx_mock: MockRouter) -> None:
+ # Test that the default follow_redirects=True allows following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+ respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+
+ response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ assert response.status_code == 200
+ assert response.json() == {"status": "ok"}
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None:
+ # Test that follow_redirects=False prevents following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+
+ with pytest.raises(APIStatusError) as exc_info:
+ await self.client.post(
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
+ )
+
+ assert exc_info.value.response.status_code == 302
+ assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
From 35fb5b644180a5d771d4d4b4fe89c99a744f2994 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 3 Jun 2025 03:37:25 +0000
Subject: [PATCH 11/11] release: 0.2.0-alpha.63
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 29 +++++++++++++++++++++++++++++
pyproject.toml | 2 +-
src/openlayer/_version.py | 2 +-
4 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 5d9c21c9..fd599489 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.2.0-alpha.62"
+ ".": "0.2.0-alpha.63"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b10d5f5..d322990b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## 0.2.0-alpha.63 (2025-06-03)
+
+Full Changelog: [v0.2.0-alpha.62...v0.2.0-alpha.63](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.62...v0.2.0-alpha.63)
+
+### Features
+
+* add MLflow notebook example ([149e85f](https://github.com/openlayer-ai/openlayer-python/commit/149e85f075db80c9800fd8dff58b277341a3384c))
+* add OpenLIT notebook example ([f71c668](https://github.com/openlayer-ai/openlayer-python/commit/f71c66895d38b0245f8a5da4c000e6bf747ef4c8))
+* **client:** add follow_redirects request option ([87d8986](https://github.com/openlayer-ai/openlayer-python/commit/87d89863dd9c4f700b8a8910ce14d2a961404336))
+
+
+### Bug Fixes
+
+* **package:** support direct resource imports ([8407753](https://github.com/openlayer-ai/openlayer-python/commit/84077531a8491bc48c8fe5d67a9076a27ba21fce))
+
+
+### Chores
+
+* **ci:** fix installation instructions ([d7d4fd2](https://github.com/openlayer-ai/openlayer-python/commit/d7d4fd2e5464f87660a30edd1067aef930b2249a))
+* **ci:** upload sdks to package manager ([0aadb0a](https://github.com/openlayer-ai/openlayer-python/commit/0aadb0a4deed48d46981fd44b308fba5bbc5a3c1))
+* **docs:** grammar improvements ([27794bc](https://github.com/openlayer-ai/openlayer-python/commit/27794bc2ff2f34c10c1635fcf14677e0711a8af0))
+* **docs:** remove reference to rye shell ([9f8db4a](https://github.com/openlayer-ai/openlayer-python/commit/9f8db4a42a79af923d55ec636e43bf49ce80bc50))
+* **internal:** avoid errors for isinstance checks on proxies ([3de384b](https://github.com/openlayer-ai/openlayer-python/commit/3de384be80ba27ba97a6079a78b75cdeadf55e5f))
+* **internal:** codegen related update ([120114a](https://github.com/openlayer-ai/openlayer-python/commit/120114ad9d40ce7c41112522f2951dd92be61eaf))
+* **internal:** codegen related update ([f990977](https://github.com/openlayer-ai/openlayer-python/commit/f990977209f13f02b1b87ab98bef5eef50414ea9))
+* link to OpenLLMetry integration guide ([ffcd085](https://github.com/openlayer-ai/openlayer-python/commit/ffcd085e1ad58e2b88fac6f739b6a9a12ba05844))
+* remove MLflow example ([17256c9](https://github.com/openlayer-ai/openlayer-python/commit/17256c96873cef5b085400ad64af860c35de4cf4))
+* sync repo ([caa47dc](https://github.com/openlayer-ai/openlayer-python/commit/caa47dc5b9d671046dca4dd5378a72018ed5d334))
+
## 0.2.0-alpha.62 (2025-04-29)
Full Changelog: [v0.2.0-alpha.61...v0.2.0-alpha.62](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.61...v0.2.0-alpha.62)
diff --git a/pyproject.toml b/pyproject.toml
index 7333be69..99b45518 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openlayer"
-version = "0.2.0-alpha.62"
+version = "0.2.0-alpha.63"
description = "The official Python library for the openlayer API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/openlayer/_version.py b/src/openlayer/_version.py
index e013ded0..37ec914f 100644
--- a/src/openlayer/_version.py
+++ b/src/openlayer/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "openlayer"
-__version__ = "0.2.0-alpha.62" # x-release-please-version
+__version__ = "0.2.0-alpha.63" # x-release-please-version