diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
deleted file mode 100644
index 5dcdfce..0000000
--- a/.github/workflows/release-doctor.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Release Doctor
-on:
- push:
- branches:
- - main
- workflow_dispatch:
-
-jobs:
- release_doctor:
- name: release doctor
- runs-on: ubuntu-latest
- environment: production
- if: github.repository == 'cryptechdev/neptune-api-v2-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
-
- steps:
- - uses: actions/checkout@v6
-
- - name: Check release environment
- run: |
- bash ./bin/check-release-environment
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index da59f99..2aca35a 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.4.0"
+ ".": "0.5.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 511af12..03d410d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 49
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cryptech%2Fneptune-api-v2-99a21fdf9159c28a75eb60c0b7ad4710d01b0a98dd0474267a07e9914039fa83.yml
-openapi_spec_hash: f58f326c00c34bc45b28b09b9530566c
-config_hash: 27aff5f3f84397a9b3c2cb8a3c1d1e71
+configured_endpoints: 50
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cryptech%2Fneptune-api-v2-40d8045cda417d4c8a7a4a0d014345cdeb273fb5fdfa48eea08493bc5a5079ce.yml
+openapi_spec_hash: 71ca66dcf6e775aae3f49b42cd6ce2cc
+config_hash: f5ca3bc259f95069f0db4114d34e29ef
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cfd6e22..455acfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## 0.5.0 (2026-04-17)
+
+Full Changelog: [v0.4.0...v0.5.0](https://github.com/cryptechdev/neptune-api-v2-python/compare/v0.4.0...v0.5.0)
+
+### Features
+
+* **api:** add TVL, missing balance variants ([10486d5](https://github.com/cryptechdev/neptune-api-v2-python/commit/10486d58a60c4e1744125c00f7cee3b8eb34382e))
+
+
+### Bug Fixes
+
+* ensure file data are only sent as 1 parameter ([1581f6d](https://github.com/cryptechdev/neptune-api-v2-python/commit/1581f6dae176d8896f9d7dcfc79cda9413aa7275))
+
+
+### Chores
+
+* **ci:** remove release-doctor workflow ([39b2df7](https://github.com/cryptechdev/neptune-api-v2-python/commit/39b2df7b359adc03bcad1e4f6521ace263c88393))
+
## 0.4.0 (2026-04-09)
Full Changelog: [v0.3.0...v0.4.0](https://github.com/cryptechdev/neptune-api-v2-python/compare/v0.3.0...v0.4.0)
diff --git a/api.md b/api.md
index c89631f..42ec630 100644
--- a/api.md
+++ b/api.md
@@ -63,10 +63,12 @@ from neptune_api_v2.types import (
GlobalMarketConfig,
MarketRate,
MergedMarket,
+ Tvl,
MarketGetMergedResponse,
MarketGetMergedByAssetResponse,
MarketGetOverviewResponse,
MarketGetParamsResponse,
+ MarketGetTvlResponse,
)
```
@@ -76,6 +78,7 @@ Methods:
- client.markets.get_merged_by_asset(\*\*params) -> MarketGetMergedByAssetResponse
- client.markets.get_overview(\*\*params) -> MarketGetOverviewResponse
- client.markets.get_params(\*\*params) -> MarketGetParamsResponse
+- client.markets.get_tvl(\*\*params) -> MarketGetTvlResponse
## Lend
diff --git a/bin/check-release-environment b/bin/check-release-environment
deleted file mode 100644
index 1e951e9..0000000
--- a/bin/check-release-environment
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-errors=()
-
-lenErrors=${#errors[@]}
-
-if [[ lenErrors -gt 0 ]]; then
- echo -e "Found the following errors in the release environment:\n"
-
- for error in "${errors[@]}"; do
- echo -e "- $error\n"
- done
-
- exit 1
-fi
-
-echo "The environment is ready to push releases!"
diff --git a/pyproject.toml b/pyproject.toml
index c3feb42..09e2885 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "neptune_api_v2"
-version = "0.4.0"
+version = "0.5.0"
description = "The official Python library for the neptune-api-v2 API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/neptune_api_v2/_utils/_utils.py b/src/neptune_api_v2/_utils/_utils.py
index eec7f4a..63b8cd6 100644
--- a/src/neptune_api_v2/_utils/_utils.py
+++ b/src/neptune_api_v2/_utils/_utils.py
@@ -86,8 +86,9 @@ def _extract_items(
index += 1
if is_dict(obj):
try:
- # We are at the last entry in the path so we must remove the field
- if (len(path)) == index:
+ # Remove the field if there are no more dict keys in the path,
+ # only "" traversal markers or end.
+ if all(p == "" for p in path[index:]):
item = obj.pop(key)
else:
item = obj[key]
diff --git a/src/neptune_api_v2/_version.py b/src/neptune_api_v2/_version.py
index aa48814..8269137 100644
--- a/src/neptune_api_v2/_version.py
+++ b/src/neptune_api_v2/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "neptune_api_v2"
-__version__ = "0.4.0" # x-release-please-version
+__version__ = "0.5.0" # x-release-please-version
diff --git a/src/neptune_api_v2/resources/markets/markets.py b/src/neptune_api_v2/resources/markets/markets.py
index 95d3787..e5320fc 100644
--- a/src/neptune_api_v2/resources/markets/markets.py
+++ b/src/neptune_api_v2/resources/markets/markets.py
@@ -13,6 +13,7 @@
AsyncLendResourceWithStreamingResponse,
)
from ...types import (
+ market_get_tvl_params,
market_get_merged_params,
market_get_params_params,
market_get_overview_params,
@@ -37,6 +38,7 @@
AsyncBorrowResourceWithStreamingResponse,
)
from ..._base_client import make_request_options
+from ...types.market_get_tvl_response import MarketGetTvlResponse
from ...types.market_get_merged_response import MarketGetMergedResponse
from ...types.market_get_params_response import MarketGetParamsResponse
from ...types.market_get_overview_response import MarketGetOverviewResponse
@@ -252,6 +254,43 @@ def get_params(
cast_to=MarketGetParamsResponse,
)
+ def get_tvl(
+ self,
+ *,
+ with_text: bool | 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,
+ ) -> MarketGetTvlResponse:
+ """
+ Get market TVL
+
+ Args:
+ with_text: Include text variation fields
+
+ 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(
+ "/api/v1/markets/tvl",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"with_text": with_text}, market_get_tvl_params.MarketGetTvlParams),
+ ),
+ cast_to=MarketGetTvlResponse,
+ )
+
class AsyncMarketsResource(AsyncAPIResource):
@cached_property
@@ -462,6 +501,43 @@ async def get_params(
cast_to=MarketGetParamsResponse,
)
+ async def get_tvl(
+ self,
+ *,
+ with_text: bool | 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,
+ ) -> MarketGetTvlResponse:
+ """
+ Get market TVL
+
+ Args:
+ with_text: Include text variation fields
+
+ 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(
+ "/api/v1/markets/tvl",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"with_text": with_text}, market_get_tvl_params.MarketGetTvlParams),
+ ),
+ cast_to=MarketGetTvlResponse,
+ )
+
class MarketsResourceWithRawResponse:
def __init__(self, markets: MarketsResource) -> None:
@@ -479,6 +555,9 @@ def __init__(self, markets: MarketsResource) -> None:
self.get_params = to_raw_response_wrapper(
markets.get_params,
)
+ self.get_tvl = to_raw_response_wrapper(
+ markets.get_tvl,
+ )
@cached_property
def lend(self) -> LendResourceWithRawResponse:
@@ -505,6 +584,9 @@ def __init__(self, markets: AsyncMarketsResource) -> None:
self.get_params = async_to_raw_response_wrapper(
markets.get_params,
)
+ self.get_tvl = async_to_raw_response_wrapper(
+ markets.get_tvl,
+ )
@cached_property
def lend(self) -> AsyncLendResourceWithRawResponse:
@@ -531,6 +613,9 @@ def __init__(self, markets: MarketsResource) -> None:
self.get_params = to_streamed_response_wrapper(
markets.get_params,
)
+ self.get_tvl = to_streamed_response_wrapper(
+ markets.get_tvl,
+ )
@cached_property
def lend(self) -> LendResourceWithStreamingResponse:
@@ -557,6 +642,9 @@ def __init__(self, markets: AsyncMarketsResource) -> None:
self.get_params = async_to_streamed_response_wrapper(
markets.get_params,
)
+ self.get_tvl = async_to_streamed_response_wrapper(
+ markets.get_tvl,
+ )
@cached_property
def lend(self) -> AsyncLendResourceWithStreamingResponse:
diff --git a/src/neptune_api_v2/types/__init__.py b/src/neptune_api_v2/types/__init__.py
index 40c7afc..98dbf87 100644
--- a/src/neptune_api_v2/types/__init__.py
+++ b/src/neptune_api_v2/types/__init__.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from .tvl import Tvl as Tvl
from .user import User as User
from .user_tx import UserTx as UserTx
from .interval import Interval as Interval
@@ -26,9 +27,11 @@
from .asset_classification import AssetClassification as AssetClassification
from .global_market_config import GlobalMarketConfig as GlobalMarketConfig
from .user_get_user_params import UserGetUserParams as UserGetUserParams
+from .market_get_tvl_params import MarketGetTvlParams as MarketGetTvlParams
from .nept_get_state_params import NeptGetStateParams as NeptGetStateParams
from .nept_get_params_params import NeptGetParamsParams as NeptGetParamsParams
from .user_get_user_response import UserGetUserResponse as UserGetUserResponse
+from .market_get_tvl_response import MarketGetTvlResponse as MarketGetTvlResponse
from .nept_get_state_response import NeptGetStateResponse as NeptGetStateResponse
from .validation_field_source import ValidationFieldSource as ValidationFieldSource
from .asset_list_prices_params import AssetListPricesParams as AssetListPricesParams
diff --git a/src/neptune_api_v2/types/market_get_overview_response.py b/src/neptune_api_v2/types/market_get_overview_response.py
index 16d25d2..b54bfba 100644
--- a/src/neptune_api_v2/types/market_get_overview_response.py
+++ b/src/neptune_api_v2/types/market_get_overview_response.py
@@ -2,6 +2,7 @@
from typing import List
+from .tvl import Tvl
from .._models import BaseModel
from .markets.lend_market import LendMarket
from .global_market_config import GlobalMarketConfig
@@ -20,6 +21,9 @@ class Data(BaseModel):
lend: List[LendMarket]
"""Current lending markets"""
+ tvl: Tvl
+ """Market TVL"""
+
class MarketGetOverviewResponse(BaseModel):
data: Data
diff --git a/src/neptune_api_v2/types/market_get_tvl_params.py b/src/neptune_api_v2/types/market_get_tvl_params.py
new file mode 100644
index 0000000..52186ab
--- /dev/null
+++ b/src/neptune_api_v2/types/market_get_tvl_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["MarketGetTvlParams"]
+
+
+class MarketGetTvlParams(TypedDict, total=False):
+ with_text: bool
+ """Include text variation fields"""
diff --git a/src/neptune_api_v2/types/market_get_tvl_response.py b/src/neptune_api_v2/types/market_get_tvl_response.py
new file mode 100644
index 0000000..c409ed2
--- /dev/null
+++ b/src/neptune_api_v2/types/market_get_tvl_response.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .tvl import Tvl
+from .._models import BaseModel
+
+__all__ = ["MarketGetTvlResponse"]
+
+
+class MarketGetTvlResponse(BaseModel):
+ data: Tvl
+
+ error: None = None
+ """Error data. Guaranteed `null` for successful response."""
+
+ status: int
+ """HTTP status.
+
+ Successful responses are guaranteed to be < `400`. Conversely, error responses
+ are guaranteed to be >= `400`.
+ """
+
+ status_text: str
+ """HTTP status text"""
diff --git a/src/neptune_api_v2/types/markets/borrow/borrow_collateral_state.py b/src/neptune_api_v2/types/markets/borrow/borrow_collateral_state.py
index aaa3ea4..6797d88 100644
--- a/src/neptune_api_v2/types/markets/borrow/borrow_collateral_state.py
+++ b/src/neptune_api_v2/types/markets/borrow/borrow_collateral_state.py
@@ -13,6 +13,8 @@ class ExtraText(BaseModel):
Will not be null when query param `with_text` is `true`.
"""
+ balance: str
+
collateral_sum: str
@@ -22,6 +24,8 @@ class ExtraValueExtraText(BaseModel):
Will not be null when query params `with_text` and `with_value` are `true`.
"""
+ balance: str
+
collateral_sum: str
price: str
@@ -48,6 +52,8 @@ class ExtraValue(BaseModel):
The embedded text group will contain the text variant if `with_text` was specified as well.
"""
+ balance: str
+
collateral_sum: str
extra: ExtraValueExtra
diff --git a/src/neptune_api_v2/types/tvl.py b/src/neptune_api_v2/types/tvl.py
new file mode 100644
index 0000000..0e6aeaf
--- /dev/null
+++ b/src/neptune_api_v2/types/tvl.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["Tvl", "Extra", "ExtraText"]
+
+
+class ExtraText(BaseModel):
+ """Human-readable field variants.
+
+ Will not be null when query param `with_text` is `true`.
+ """
+
+ collateral_value: str
+
+ lend_value: str
+
+ total_value: str
+
+
+class Extra(BaseModel):
+ text: Optional[ExtraText] = None
+ """Human-readable field variants.
+
+ Will not be null when query param `with_text` is `true`.
+ """
+
+
+class Tvl(BaseModel):
+ collateral_value: str
+ """Market TVL in USD - collateral portion"""
+
+ extra: Extra
+
+ lend_value: str
+ """Market TVL in USD - lend portion"""
+
+ total_value: str
+ """Market TVL in USD"""
diff --git a/tests/api_resources/test_markets.py b/tests/api_resources/test_markets.py
index 0a5f4cf..4574adc 100644
--- a/tests/api_resources/test_markets.py
+++ b/tests/api_resources/test_markets.py
@@ -10,6 +10,7 @@
from tests.utils import assert_matches_type
from neptune_api_v2 import NeptuneAPIV2, AsyncNeptuneAPIV2
from neptune_api_v2.types import (
+ MarketGetTvlResponse,
MarketGetMergedResponse,
MarketGetParamsResponse,
MarketGetOverviewResponse,
@@ -176,6 +177,42 @@ def test_streaming_response_get_params(self, client: NeptuneAPIV2) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_get_tvl(self, client: NeptuneAPIV2) -> None:
+ market = client.markets.get_tvl()
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_get_tvl_with_all_params(self, client: NeptuneAPIV2) -> None:
+ market = client.markets.get_tvl(
+ with_text=True,
+ )
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_get_tvl(self, client: NeptuneAPIV2) -> None:
+ response = client.markets.with_raw_response.get_tvl()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ market = response.parse()
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_get_tvl(self, client: NeptuneAPIV2) -> None:
+ with client.markets.with_streaming_response.get_tvl() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ market = response.parse()
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
class TestAsyncMarkets:
parametrize = pytest.mark.parametrize(
@@ -335,3 +372,39 @@ async def test_streaming_response_get_params(self, async_client: AsyncNeptuneAPI
assert_matches_type(MarketGetParamsResponse, market, path=["response"])
assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_get_tvl(self, async_client: AsyncNeptuneAPIV2) -> None:
+ market = await async_client.markets.get_tvl()
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_get_tvl_with_all_params(self, async_client: AsyncNeptuneAPIV2) -> None:
+ market = await async_client.markets.get_tvl(
+ with_text=True,
+ )
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_get_tvl(self, async_client: AsyncNeptuneAPIV2) -> None:
+ response = await async_client.markets.with_raw_response.get_tvl()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ market = await response.parse()
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_get_tvl(self, async_client: AsyncNeptuneAPIV2) -> None:
+ async with async_client.markets.with_streaming_response.get_tvl() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ market = await response.parse()
+ assert_matches_type(MarketGetTvlResponse, market, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py
index e94f713..587f87d 100644
--- a/tests/test_extract_files.py
+++ b/tests/test_extract_files.py
@@ -35,6 +35,15 @@ def test_multiple_files() -> None:
assert query == {"documents": [{}, {}]}
+def test_top_level_file_array() -> None:
+ query = {"files": [b"file one", b"file two"], "title": "hello"}
+ assert extract_files(query, paths=[["files", ""]]) == [
+ ("files[]", b"file one"),
+ ("files[]", b"file two"),
+ ]
+ assert query == {"title": "hello"}
+
+
@pytest.mark.parametrize(
"query,paths,expected",
[