diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 693d080d..edd79e36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 577aef52..c523ce19 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.7.12" + ".": "1.8.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 6a38ffdc..7ea75d24 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 125 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/letta-ai%2Fletta-sdk-1c44d73b9152645e7b44512a238467b88c993a2c0151d5911b7f98d05583790e.yml -openapi_spec_hash: e0633c52cd8d694e72211de731d1354a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/letta-ai%2Fletta-sdk-4fd045c6d837048eb53e33ebdbc1add45ecdf2bd619c75b2d3b2ae5de5ea5e38.yml +openapi_spec_hash: eba6436aa8bea2851a561a9412d9e13d config_hash: 2dd2cc848568d7bec38b1cc774a5900c diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c0fbb2..a0f897d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## 1.8.0 (2026-03-19) + +Full Changelog: [v1.7.12...v1.8.0](https://github.com/letta-ai/letta-python/compare/v1.7.12...v1.8.0) + +### Features + +* add zai coding provider ([192fc35](https://github.com/letta-ai/letta-python/commit/192fc354c262fca4e4562816198006e92da1b1b4)) +* always create otid for request ([f208278](https://github.com/letta-ai/letta-python/commit/f20827887378a6a0525d02731436c820e3c6ee06)) + + +### Bug Fixes + +* **deps:** bump minimum typing-extensions version ([fcebea1](https://github.com/letta-ai/letta-python/commit/fcebea132a9cfa2f4d87ab298ccdc32cf58f6884)) +* **pydantic:** do not pass `by_alias` unless set ([6fd6fc5](https://github.com/letta-ai/letta-python/commit/6fd6fc5ab183d7a0eef902bb07f3ea4801b1e247)) +* sanitize endpoint path params ([bc0615a](https://github.com/letta-ai/letta-python/commit/bc0615aa724b78328ceb554685fd5c47457e3633)) + + +### Chores + +* **internal:** tweak CI branches ([0591087](https://github.com/letta-ai/letta-python/commit/05910876a703bbd272924a3c54d9a8fe2c993c94)) + + +### Refactors + +* **sglang:** use input_ids instead of text in native adapter ([66c6a22](https://github.com/letta-ai/letta-python/commit/66c6a223d1c3aa6b081289401185d528218efdba)) + ## 1.7.12 (2026-03-09) Full Changelog: [v1.7.11...v1.7.12](https://github.com/letta-ai/letta-python/compare/v1.7.11...v1.7.12) diff --git a/pyproject.toml b/pyproject.toml index 385a292e..49ccc1d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "letta-client" -version = "1.7.12" +version = "1.8.0" description = "The official Python library for the letta API" dynamic = ["readme"] license = "Apache-2.0" @@ -11,7 +11,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", + "typing-extensions>=4.14, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", diff --git a/src/letta_client/_compat.py b/src/letta_client/_compat.py index 786ff42a..e6690a4f 100644 --- a/src/letta_client/_compat.py +++ b/src/letta_client/_compat.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime -from typing_extensions import Self, Literal +from typing_extensions import Self, Literal, TypedDict import pydantic from pydantic.fields import FieldInfo @@ -131,6 +131,10 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: return model.model_dump_json(indent=indent) +class _ModelDumpKwargs(TypedDict, total=False): + by_alias: bool + + def model_dump( model: pydantic.BaseModel, *, @@ -142,6 +146,9 @@ def model_dump( by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + kwargs: _ModelDumpKwargs = {} + if by_alias is not None: + kwargs["by_alias"] = by_alias return model.model_dump( mode=mode, exclude=exclude, @@ -149,7 +156,7 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, - by_alias=by_alias, + **kwargs, ) return cast( "dict[str, Any]", diff --git a/src/letta_client/_utils/__init__.py b/src/letta_client/_utils/__init__.py index dc64e29a..10cb66d2 100644 --- a/src/letta_client/_utils/__init__.py +++ b/src/letta_client/_utils/__init__.py @@ -1,3 +1,4 @@ +from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( diff --git a/src/letta_client/_utils/_path.py b/src/letta_client/_utils/_path.py new file mode 100644 index 00000000..4d6e1e4c --- /dev/null +++ b/src/letta_client/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.3 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/src/letta_client/_version.py b/src/letta_client/_version.py index e80b6469..d6b43cb6 100644 --- a/src/letta_client/_version.py +++ b/src/letta_client/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "letta_client" -__version__ = "1.7.12" # x-release-please-version +__version__ = "1.8.0" # x-release-please-version diff --git a/src/letta_client/resources/access_tokens.py b/src/letta_client/resources/access_tokens.py index 94ff9c99..bb8e2c43 100644 --- a/src/letta_client/resources/access_tokens.py +++ b/src/letta_client/resources/access_tokens.py @@ -8,7 +8,7 @@ from ..types import access_token_list_params, access_token_create_params, access_token_delete_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -171,7 +171,7 @@ def delete( if not token: raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") return self._delete( - f"/v1/client-side-access-tokens/{token}", + path_template("/v1/client-side-access-tokens/{token}", token=token), body=maybe_transform(body, access_token_delete_params.AccessTokenDeleteParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -327,7 +327,7 @@ async def delete( if not token: raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") return await self._delete( - f"/v1/client-side-access-tokens/{token}", + path_template("/v1/client-side-access-tokens/{token}", token=token), body=await async_maybe_transform(body, access_token_delete_params.AccessTokenDeleteParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/letta_client/resources/agents/agents.py b/src/letta_client/resources/agents/agents.py index 4633616c..bbd1f191 100644 --- a/src/letta_client/resources/agents/agents.py +++ b/src/letta_client/resources/agents/agents.py @@ -62,7 +62,14 @@ omit, not_given, ) -from ..._utils import extract_files, maybe_transform, strip_not_given, deepcopy_minimal, async_maybe_transform +from ..._utils import ( + extract_files, + path_template, + maybe_transform, + strip_not_given, + deepcopy_minimal, + async_maybe_transform, +) from .archives import ( ArchivesResource, AsyncArchivesResource, @@ -484,7 +491,7 @@ def retrieve( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get( - f"/v1/agents/{agent_id}", + path_template("/v1/agents/{agent_id}", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -657,7 +664,7 @@ def update( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._patch( - f"/v1/agents/{agent_id}", + path_template("/v1/agents/{agent_id}", agent_id=agent_id), body=maybe_transform( { "base_template_id": base_template_id, @@ -868,7 +875,7 @@ def delete( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._delete( - f"/v1/agents/{agent_id}", + path_template("/v1/agents/{agent_id}", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -916,7 +923,7 @@ def export_file( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get( - f"/v1/agents/{agent_id}/export", + path_template("/v1/agents/{agent_id}/export", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1074,7 +1081,7 @@ def recompile( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/recompile", + path_template("/v1/agents/{agent_id}/recompile", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1451,7 +1458,7 @@ async def retrieve( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._get( - f"/v1/agents/{agent_id}", + path_template("/v1/agents/{agent_id}", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1624,7 +1631,7 @@ async def update( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._patch( - f"/v1/agents/{agent_id}", + path_template("/v1/agents/{agent_id}", agent_id=agent_id), body=await async_maybe_transform( { "base_template_id": base_template_id, @@ -1835,7 +1842,7 @@ async def delete( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._delete( - f"/v1/agents/{agent_id}", + path_template("/v1/agents/{agent_id}", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1883,7 +1890,7 @@ async def export_file( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._get( - f"/v1/agents/{agent_id}/export", + path_template("/v1/agents/{agent_id}/export", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2041,7 +2048,7 @@ async def recompile( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/recompile", + path_template("/v1/agents/{agent_id}/recompile", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/letta_client/resources/agents/archives.py b/src/letta_client/resources/agents/archives.py index 0af1d1e9..037c5cdc 100644 --- a/src/letta_client/resources/agents/archives.py +++ b/src/letta_client/resources/agents/archives.py @@ -5,6 +5,7 @@ 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 ( @@ -69,7 +70,9 @@ def attach( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return self._patch( - f"/v1/agents/{agent_id}/archives/attach/{archive_id}", + path_template( + "/v1/agents/{agent_id}/archives/attach/{archive_id}", agent_id=agent_id, archive_id=archive_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -107,7 +110,9 @@ def detach( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return self._patch( - f"/v1/agents/{agent_id}/archives/detach/{archive_id}", + path_template( + "/v1/agents/{agent_id}/archives/detach/{archive_id}", agent_id=agent_id, archive_id=archive_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -166,7 +171,9 @@ async def attach( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/archives/attach/{archive_id}", + path_template( + "/v1/agents/{agent_id}/archives/attach/{archive_id}", agent_id=agent_id, archive_id=archive_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -204,7 +211,9 @@ async def detach( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/archives/detach/{archive_id}", + path_template( + "/v1/agents/{agent_id}/archives/detach/{archive_id}", agent_id=agent_id, archive_id=archive_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/agents/blocks.py b/src/letta_client/resources/agents/blocks.py index 499ecc4f..5e670eae 100644 --- a/src/letta_client/resources/agents/blocks.py +++ b/src/letta_client/resources/agents/blocks.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -77,7 +77,9 @@ def retrieve( if not block_label: raise ValueError(f"Expected a non-empty value for `block_label` but received {block_label!r}") return self._get( - f"/v1/agents/{agent_id}/core-memory/blocks/{block_label}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/{block_label}", agent_id=agent_id, block_label=block_label + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -163,7 +165,9 @@ def update( if not block_label: raise ValueError(f"Expected a non-empty value for `block_label` but received {block_label!r}") return self._patch( - f"/v1/agents/{agent_id}/core-memory/blocks/{block_label}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/{block_label}", agent_id=agent_id, block_label=block_label + ), body=maybe_transform( { "base_template_id": base_template_id, @@ -237,7 +241,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/core-memory/blocks", + path_template("/v1/agents/{agent_id}/core-memory/blocks", agent_id=agent_id), page=SyncArrayPage[BlockResponse], options=make_request_options( extra_headers=extra_headers, @@ -291,7 +295,9 @@ def attach( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._patch( - f"/v1/agents/{agent_id}/core-memory/blocks/attach/{block_id}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/attach/{block_id}", agent_id=agent_id, block_id=block_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -331,7 +337,9 @@ def detach( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._patch( - f"/v1/agents/{agent_id}/core-memory/blocks/detach/{block_id}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/detach/{block_id}", agent_id=agent_id, block_id=block_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -390,7 +398,9 @@ async def retrieve( if not block_label: raise ValueError(f"Expected a non-empty value for `block_label` but received {block_label!r}") return await self._get( - f"/v1/agents/{agent_id}/core-memory/blocks/{block_label}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/{block_label}", agent_id=agent_id, block_label=block_label + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -476,7 +486,9 @@ async def update( if not block_label: raise ValueError(f"Expected a non-empty value for `block_label` but received {block_label!r}") return await self._patch( - f"/v1/agents/{agent_id}/core-memory/blocks/{block_label}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/{block_label}", agent_id=agent_id, block_label=block_label + ), body=await async_maybe_transform( { "base_template_id": base_template_id, @@ -550,7 +562,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/core-memory/blocks", + path_template("/v1/agents/{agent_id}/core-memory/blocks", agent_id=agent_id), page=AsyncArrayPage[BlockResponse], options=make_request_options( extra_headers=extra_headers, @@ -604,7 +616,9 @@ async def attach( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/core-memory/blocks/attach/{block_id}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/attach/{block_id}", agent_id=agent_id, block_id=block_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -644,7 +658,9 @@ async def detach( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/core-memory/blocks/detach/{block_id}", + path_template( + "/v1/agents/{agent_id}/core-memory/blocks/detach/{block_id}", agent_id=agent_id, block_id=block_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/agents/files.py b/src/letta_client/resources/agents/files.py index 68dacdce..338e4f3a 100644 --- a/src/letta_client/resources/agents/files.py +++ b/src/letta_client/resources/agents/files.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -99,7 +99,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/files", + path_template("/v1/agents/{agent_id}/files", agent_id=agent_id), page=SyncNextFilesPage[FileListResponse], options=make_request_options( extra_headers=extra_headers, @@ -158,7 +158,7 @@ def close( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._patch( - f"/v1/agents/{agent_id}/files/{file_id}/close", + path_template("/v1/agents/{agent_id}/files/{file_id}/close", agent_id=agent_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -196,7 +196,7 @@ def close_all( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._patch( - f"/v1/agents/{agent_id}/files/close-all", + path_template("/v1/agents/{agent_id}/files/close-all", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -240,7 +240,7 @@ def open( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._patch( - f"/v1/agents/{agent_id}/files/{file_id}/open", + path_template("/v1/agents/{agent_id}/files/{file_id}/open", agent_id=agent_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -320,7 +320,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/files", + path_template("/v1/agents/{agent_id}/files", agent_id=agent_id), page=AsyncNextFilesPage[FileListResponse], options=make_request_options( extra_headers=extra_headers, @@ -379,7 +379,7 @@ async def close( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/files/{file_id}/close", + path_template("/v1/agents/{agent_id}/files/{file_id}/close", agent_id=agent_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -417,7 +417,7 @@ async def close_all( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/files/close-all", + path_template("/v1/agents/{agent_id}/files/close-all", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -461,7 +461,7 @@ async def open( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/files/{file_id}/open", + path_template("/v1/agents/{agent_id}/files/{file_id}/open", agent_id=agent_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/agents/folders.py b/src/letta_client/resources/agents/folders.py index ad25c9ea..2cc0c9d1 100644 --- a/src/letta_client/resources/agents/folders.py +++ b/src/letta_client/resources/agents/folders.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -92,7 +92,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/folders", + path_template("/v1/agents/{agent_id}/folders", agent_id=agent_id), page=SyncArrayPage[FolderListResponse], options=make_request_options( extra_headers=extra_headers, @@ -146,7 +146,7 @@ def attach( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._patch( - f"/v1/agents/{agent_id}/folders/attach/{folder_id}", + path_template("/v1/agents/{agent_id}/folders/attach/{folder_id}", agent_id=agent_id, folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -186,7 +186,7 @@ def detach( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._patch( - f"/v1/agents/{agent_id}/folders/detach/{folder_id}", + path_template("/v1/agents/{agent_id}/folders/detach/{folder_id}", agent_id=agent_id, folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -260,7 +260,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/folders", + path_template("/v1/agents/{agent_id}/folders", agent_id=agent_id), page=AsyncArrayPage[FolderListResponse], options=make_request_options( extra_headers=extra_headers, @@ -314,7 +314,7 @@ async def attach( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/folders/attach/{folder_id}", + path_template("/v1/agents/{agent_id}/folders/attach/{folder_id}", agent_id=agent_id, folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -354,7 +354,7 @@ async def detach( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/folders/detach/{folder_id}", + path_template("/v1/agents/{agent_id}/folders/detach/{folder_id}", agent_id=agent_id, folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/agents/identities.py b/src/letta_client/resources/agents/identities.py index b7beaef2..bfa78582 100644 --- a/src/letta_client/resources/agents/identities.py +++ b/src/letta_client/resources/agents/identities.py @@ -5,6 +5,7 @@ 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 ( @@ -69,7 +70,9 @@ def attach( if not identity_id: raise ValueError(f"Expected a non-empty value for `identity_id` but received {identity_id!r}") return self._patch( - f"/v1/agents/{agent_id}/identities/attach/{identity_id}", + path_template( + "/v1/agents/{agent_id}/identities/attach/{identity_id}", agent_id=agent_id, identity_id=identity_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -107,7 +110,9 @@ def detach( if not identity_id: raise ValueError(f"Expected a non-empty value for `identity_id` but received {identity_id!r}") return self._patch( - f"/v1/agents/{agent_id}/identities/detach/{identity_id}", + path_template( + "/v1/agents/{agent_id}/identities/detach/{identity_id}", agent_id=agent_id, identity_id=identity_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -166,7 +171,9 @@ async def attach( if not identity_id: raise ValueError(f"Expected a non-empty value for `identity_id` but received {identity_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/identities/attach/{identity_id}", + path_template( + "/v1/agents/{agent_id}/identities/attach/{identity_id}", agent_id=agent_id, identity_id=identity_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -204,7 +211,9 @@ async def detach( if not identity_id: raise ValueError(f"Expected a non-empty value for `identity_id` but received {identity_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/identities/detach/{identity_id}", + path_template( + "/v1/agents/{agent_id}/identities/detach/{identity_id}", agent_id=agent_id, identity_id=identity_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/agents/messages.py b/src/letta_client/resources/agents/messages.py index d3ea7f68..b28fcbf7 100644 --- a/src/letta_client/resources/agents/messages.py +++ b/src/letta_client/resources/agents/messages.py @@ -9,7 +9,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -482,7 +482,7 @@ def create( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/messages", + path_template("/v1/agents/{agent_id}/messages", agent_id=agent_id), body=maybe_transform( { "assistant_message_tool_kwarg": assistant_message_tool_kwarg, @@ -582,7 +582,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/messages", + path_template("/v1/agents/{agent_id}/messages", agent_id=agent_id), page=SyncArrayPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -644,7 +644,7 @@ def cancel( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/messages/cancel", + path_template("/v1/agents/{agent_id}/messages/cancel", agent_id=agent_id), body=maybe_transform({"run_ids": run_ids}, message_cancel_params.MessageCancelParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -686,7 +686,7 @@ def compact( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/summarize", + path_template("/v1/agents/{agent_id}/summarize", agent_id=agent_id), body=maybe_transform( {"compaction_settings": compaction_settings}, message_compact_params.MessageCompactParams ), @@ -804,7 +804,7 @@ def create_async( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/messages/async", + path_template("/v1/agents/{agent_id}/messages/async", agent_id=agent_id), body=maybe_transform( { "assistant_message_tool_kwarg": assistant_message_tool_kwarg, @@ -863,7 +863,7 @@ def reset( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._patch( - f"/v1/agents/{agent_id}/reset-messages", + path_template("/v1/agents/{agent_id}/reset-messages", agent_id=agent_id), body=maybe_transform( {"add_default_initial_messages": add_default_initial_messages}, message_reset_params.MessageResetParams ), @@ -996,7 +996,7 @@ def stream( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/messages/stream", + path_template("/v1/agents/{agent_id}/messages/stream", agent_id=agent_id), body=maybe_transform( { "assistant_message_tool_kwarg": assistant_message_tool_kwarg, @@ -1472,7 +1472,7 @@ async def create( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/messages", + path_template("/v1/agents/{agent_id}/messages", agent_id=agent_id), body=await async_maybe_transform( { "assistant_message_tool_kwarg": assistant_message_tool_kwarg, @@ -1572,7 +1572,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/messages", + path_template("/v1/agents/{agent_id}/messages", agent_id=agent_id), page=AsyncArrayPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -1634,7 +1634,7 @@ async def cancel( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/messages/cancel", + path_template("/v1/agents/{agent_id}/messages/cancel", agent_id=agent_id), body=await async_maybe_transform({"run_ids": run_ids}, message_cancel_params.MessageCancelParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -1676,7 +1676,7 @@ async def compact( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/summarize", + path_template("/v1/agents/{agent_id}/summarize", agent_id=agent_id), body=await async_maybe_transform( {"compaction_settings": compaction_settings}, message_compact_params.MessageCompactParams ), @@ -1794,7 +1794,7 @@ async def create_async( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/messages/async", + path_template("/v1/agents/{agent_id}/messages/async", agent_id=agent_id), body=await async_maybe_transform( { "assistant_message_tool_kwarg": assistant_message_tool_kwarg, @@ -1853,7 +1853,7 @@ async def reset( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/reset-messages", + path_template("/v1/agents/{agent_id}/reset-messages", agent_id=agent_id), body=await async_maybe_transform( {"add_default_initial_messages": add_default_initial_messages}, message_reset_params.MessageResetParams ), @@ -1986,7 +1986,7 @@ async def stream( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/messages/stream", + path_template("/v1/agents/{agent_id}/messages/stream", agent_id=agent_id), body=await async_maybe_transform( { "assistant_message_tool_kwarg": assistant_message_tool_kwarg, diff --git a/src/letta_client/resources/agents/passages.py b/src/letta_client/resources/agents/passages.py index 0ab2754c..409704d1 100644 --- a/src/letta_client/resources/agents/passages.py +++ b/src/letta_client/resources/agents/passages.py @@ -9,7 +9,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -84,7 +84,7 @@ def create( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/archival-memory", + path_template("/v1/agents/{agent_id}/archival-memory", agent_id=agent_id), body=maybe_transform( { "text": text, @@ -143,7 +143,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get( - f"/v1/agents/{agent_id}/archival-memory", + path_template("/v1/agents/{agent_id}/archival-memory", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -194,7 +194,7 @@ def delete( if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") return self._delete( - f"/v1/agents/{agent_id}/archival-memory/{memory_id}", + path_template("/v1/agents/{agent_id}/archival-memory/{memory_id}", agent_id=agent_id, memory_id=memory_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -254,7 +254,7 @@ def search( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get( - f"/v1/agents/{agent_id}/archival-memory/search", + path_template("/v1/agents/{agent_id}/archival-memory/search", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -333,7 +333,7 @@ async def create( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/archival-memory", + path_template("/v1/agents/{agent_id}/archival-memory", agent_id=agent_id), body=await async_maybe_transform( { "text": text, @@ -392,7 +392,7 @@ async def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._get( - f"/v1/agents/{agent_id}/archival-memory", + path_template("/v1/agents/{agent_id}/archival-memory", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -443,7 +443,7 @@ async def delete( if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") return await self._delete( - f"/v1/agents/{agent_id}/archival-memory/{memory_id}", + path_template("/v1/agents/{agent_id}/archival-memory/{memory_id}", agent_id=agent_id, memory_id=memory_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -503,7 +503,7 @@ async def search( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._get( - f"/v1/agents/{agent_id}/archival-memory/search", + path_template("/v1/agents/{agent_id}/archival-memory/search", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/letta_client/resources/agents/schedule.py b/src/letta_client/resources/agents/schedule.py index fd991e05..808c8019 100644 --- a/src/letta_client/resources/agents/schedule.py +++ b/src/letta_client/resources/agents/schedule.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -92,7 +92,7 @@ def create( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._post( - f"/v1/agents/{agent_id}/schedule", + path_template("/v1/agents/{agent_id}/schedule", agent_id=agent_id), body=maybe_transform( { "messages": messages, @@ -140,7 +140,11 @@ def retrieve( f"Expected a non-empty value for `scheduled_message_id` but received {scheduled_message_id!r}" ) return self._get( - f"/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + path_template( + "/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + agent_id=agent_id, + scheduled_message_id=scheduled_message_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -175,7 +179,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get( - f"/v1/agents/{agent_id}/schedule", + path_template("/v1/agents/{agent_id}/schedule", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -223,7 +227,11 @@ def delete( f"Expected a non-empty value for `scheduled_message_id` but received {scheduled_message_id!r}" ) return self._delete( - f"/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + path_template( + "/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + agent_id=agent_id, + scheduled_message_id=scheduled_message_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -296,7 +304,7 @@ async def create( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._post( - f"/v1/agents/{agent_id}/schedule", + path_template("/v1/agents/{agent_id}/schedule", agent_id=agent_id), body=await async_maybe_transform( { "messages": messages, @@ -344,7 +352,11 @@ async def retrieve( f"Expected a non-empty value for `scheduled_message_id` but received {scheduled_message_id!r}" ) return await self._get( - f"/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + path_template( + "/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + agent_id=agent_id, + scheduled_message_id=scheduled_message_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -379,7 +391,7 @@ async def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return await self._get( - f"/v1/agents/{agent_id}/schedule", + path_template("/v1/agents/{agent_id}/schedule", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -427,7 +439,11 @@ async def delete( f"Expected a non-empty value for `scheduled_message_id` but received {scheduled_message_id!r}" ) return await self._delete( - f"/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + path_template( + "/v1/agents/{agent_id}/schedule/{scheduled_message_id}", + agent_id=agent_id, + scheduled_message_id=scheduled_message_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/agents/tools.py b/src/letta_client/resources/agents/tools.py index 10c9e7bc..91611299 100644 --- a/src/letta_client/resources/agents/tools.py +++ b/src/letta_client/resources/agents/tools.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -93,7 +93,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/tools", + path_template("/v1/agents/{agent_id}/tools", agent_id=agent_id), page=SyncArrayPage[Tool], options=make_request_options( extra_headers=extra_headers, @@ -147,7 +147,7 @@ def attach( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._patch( - f"/v1/agents/{agent_id}/tools/attach/{tool_id}", + path_template("/v1/agents/{agent_id}/tools/attach/{tool_id}", agent_id=agent_id, tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -187,7 +187,7 @@ def detach( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._patch( - f"/v1/agents/{agent_id}/tools/detach/{tool_id}", + path_template("/v1/agents/{agent_id}/tools/detach/{tool_id}", agent_id=agent_id, tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -231,7 +231,7 @@ def run( if not tool_name: raise ValueError(f"Expected a non-empty value for `tool_name` but received {tool_name!r}") return self._post( - f"/v1/agents/{agent_id}/tools/{tool_name}/run", + path_template("/v1/agents/{agent_id}/tools/{tool_name}/run", agent_id=agent_id, tool_name=tool_name), body=maybe_transform({"args": args}, tool_run_params.ToolRunParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -279,7 +279,7 @@ def update_approval( if not tool_name: raise ValueError(f"Expected a non-empty value for `tool_name` but received {tool_name!r}") return self._patch( - f"/v1/agents/{agent_id}/tools/approval/{tool_name}", + path_template("/v1/agents/{agent_id}/tools/approval/{tool_name}", agent_id=agent_id, tool_name=tool_name), body=maybe_transform( {"body_requires_approval": body_requires_approval}, tool_update_approval_params.ToolUpdateApprovalParams ), @@ -363,7 +363,7 @@ def list( if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") return self._get_api_list( - f"/v1/agents/{agent_id}/tools", + path_template("/v1/agents/{agent_id}/tools", agent_id=agent_id), page=AsyncArrayPage[Tool], options=make_request_options( extra_headers=extra_headers, @@ -417,7 +417,7 @@ async def attach( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/tools/attach/{tool_id}", + path_template("/v1/agents/{agent_id}/tools/attach/{tool_id}", agent_id=agent_id, tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -457,7 +457,7 @@ async def detach( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._patch( - f"/v1/agents/{agent_id}/tools/detach/{tool_id}", + path_template("/v1/agents/{agent_id}/tools/detach/{tool_id}", agent_id=agent_id, tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -501,7 +501,7 @@ async def run( if not tool_name: raise ValueError(f"Expected a non-empty value for `tool_name` but received {tool_name!r}") return await self._post( - f"/v1/agents/{agent_id}/tools/{tool_name}/run", + path_template("/v1/agents/{agent_id}/tools/{tool_name}/run", agent_id=agent_id, tool_name=tool_name), body=await async_maybe_transform({"args": args}, tool_run_params.ToolRunParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -549,7 +549,7 @@ async def update_approval( if not tool_name: raise ValueError(f"Expected a non-empty value for `tool_name` but received {tool_name!r}") return await self._patch( - f"/v1/agents/{agent_id}/tools/approval/{tool_name}", + path_template("/v1/agents/{agent_id}/tools/approval/{tool_name}", agent_id=agent_id, tool_name=tool_name), body=await async_maybe_transform( {"body_requires_approval": body_requires_approval}, tool_update_approval_params.ToolUpdateApprovalParams ), diff --git a/src/letta_client/resources/archives/archives.py b/src/letta_client/resources/archives/archives.py index 508c4569..137f4922 100644 --- a/src/letta_client/resources/archives/archives.py +++ b/src/letta_client/resources/archives/archives.py @@ -9,7 +9,7 @@ from ...types import archive_list_params, archive_create_params, archive_update_params from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from .passages import ( PassagesResource, AsyncPassagesResource, @@ -133,7 +133,7 @@ def retrieve( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return self._get( - f"/v1/archives/{archive_id}", + path_template("/v1/archives/{archive_id}", archive_id=archive_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -170,7 +170,7 @@ def update( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return self._patch( - f"/v1/archives/{archive_id}", + path_template("/v1/archives/{archive_id}", archive_id=archive_id), body=maybe_transform( { "description": description, @@ -284,7 +284,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/v1/archives/{archive_id}", + path_template("/v1/archives/{archive_id}", archive_id=archive_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -391,7 +391,7 @@ async def retrieve( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return await self._get( - f"/v1/archives/{archive_id}", + path_template("/v1/archives/{archive_id}", archive_id=archive_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -428,7 +428,7 @@ async def update( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return await self._patch( - f"/v1/archives/{archive_id}", + path_template("/v1/archives/{archive_id}", archive_id=archive_id), body=await async_maybe_transform( { "description": description, @@ -542,7 +542,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/v1/archives/{archive_id}", + path_template("/v1/archives/{archive_id}", archive_id=archive_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/archives/passages.py b/src/letta_client/resources/archives/passages.py index f3209334..447c9c63 100644 --- a/src/letta_client/resources/archives/passages.py +++ b/src/letta_client/resources/archives/passages.py @@ -7,7 +7,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -86,7 +86,7 @@ def create( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return self._post( - f"/v1/archives/{archive_id}/passages", + path_template("/v1/archives/{archive_id}/passages", archive_id=archive_id), body=maybe_transform( { "text": text, @@ -139,7 +139,9 @@ def delete( raise ValueError(f"Expected a non-empty value for `passage_id` but received {passage_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/v1/archives/{archive_id}/passages/{passage_id}", + path_template( + "/v1/archives/{archive_id}/passages/{passage_id}", archive_id=archive_id, passage_id=passage_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -179,7 +181,7 @@ def create_many( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return self._post( - f"/v1/archives/{archive_id}/passages/batch", + path_template("/v1/archives/{archive_id}/passages/batch", archive_id=archive_id), body=maybe_transform({"passages": passages}, passage_create_many_params.PassageCreateManyParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -250,7 +252,7 @@ async def create( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return await self._post( - f"/v1/archives/{archive_id}/passages", + path_template("/v1/archives/{archive_id}/passages", archive_id=archive_id), body=await async_maybe_transform( { "text": text, @@ -303,7 +305,9 @@ async def delete( raise ValueError(f"Expected a non-empty value for `passage_id` but received {passage_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/v1/archives/{archive_id}/passages/{passage_id}", + path_template( + "/v1/archives/{archive_id}/passages/{passage_id}", archive_id=archive_id, passage_id=passage_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -343,7 +347,7 @@ async def create_many( if not archive_id: raise ValueError(f"Expected a non-empty value for `archive_id` but received {archive_id!r}") return await self._post( - f"/v1/archives/{archive_id}/passages/batch", + path_template("/v1/archives/{archive_id}/passages/batch", archive_id=archive_id), body=await async_maybe_transform( {"passages": passages}, passage_create_many_params.PassageCreateManyParams ), diff --git a/src/letta_client/resources/blocks/agents.py b/src/letta_client/resources/blocks/agents.py index ef3f9024..04bede63 100644 --- a/src/letta_client/resources/blocks/agents.py +++ b/src/letta_client/resources/blocks/agents.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -115,7 +115,7 @@ def list( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._get_api_list( - f"/v1/blocks/{block_id}/agents", + path_template("/v1/blocks/{block_id}/agents", block_id=block_id), page=SyncArrayPage[AgentState], options=make_request_options( extra_headers=extra_headers, @@ -229,7 +229,7 @@ def list( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._get_api_list( - f"/v1/blocks/{block_id}/agents", + path_template("/v1/blocks/{block_id}/agents", block_id=block_id), page=AsyncArrayPage[AgentState], options=make_request_options( extra_headers=extra_headers, diff --git a/src/letta_client/resources/blocks/blocks.py b/src/letta_client/resources/blocks/blocks.py index ec51486a..a97a2f5a 100644 --- a/src/letta_client/resources/blocks/blocks.py +++ b/src/letta_client/resources/blocks/blocks.py @@ -17,7 +17,7 @@ ) from ...types import block_list_params, block_create_params, block_update_params from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -182,7 +182,7 @@ def retrieve( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._get( - f"/v1/blocks/{block_id}", + path_template("/v1/blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -265,7 +265,7 @@ def update( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._patch( - f"/v1/blocks/{block_id}", + path_template("/v1/blocks/{block_id}", block_id=block_id), body=maybe_transform( { "base_template_id": base_template_id, @@ -447,7 +447,7 @@ def delete( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._delete( - f"/v1/blocks/{block_id}", + path_template("/v1/blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -604,7 +604,7 @@ async def retrieve( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._get( - f"/v1/blocks/{block_id}", + path_template("/v1/blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -687,7 +687,7 @@ async def update( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._patch( - f"/v1/blocks/{block_id}", + path_template("/v1/blocks/{block_id}", block_id=block_id), body=await async_maybe_transform( { "base_template_id": base_template_id, @@ -869,7 +869,7 @@ async def delete( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._delete( - f"/v1/blocks/{block_id}", + path_template("/v1/blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/conversations/conversations.py b/src/letta_client/resources/conversations/conversations.py index 03cbff2d..0bb127c9 100644 --- a/src/letta_client/resources/conversations/conversations.py +++ b/src/letta_client/resources/conversations/conversations.py @@ -15,7 +15,7 @@ conversation_recompile_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from .messages import ( MessagesResource, AsyncMessagesResource, @@ -154,7 +154,7 @@ def retrieve( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._get( - f"/v1/conversations/{conversation_id}", + path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -200,7 +200,7 @@ def update( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._patch( - f"/v1/conversations/{conversation_id}", + path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id), body=maybe_transform( { "model": model, @@ -311,7 +311,7 @@ def delete( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._delete( - f"/v1/conversations/{conversation_id}", + path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -359,7 +359,7 @@ def cancel( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/v1/conversations/{conversation_id}/cancel", + path_template("/v1/conversations/{conversation_id}/cancel", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -413,7 +413,7 @@ def recompile( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/v1/conversations/{conversation_id}/recompile", + path_template("/v1/conversations/{conversation_id}/recompile", conversation_id=conversation_id), body=maybe_transform( { "agent_id": agent_id, @@ -548,7 +548,7 @@ async def retrieve( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._get( - f"/v1/conversations/{conversation_id}", + path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -594,7 +594,7 @@ async def update( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._patch( - f"/v1/conversations/{conversation_id}", + path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id), body=await async_maybe_transform( { "model": model, @@ -705,7 +705,7 @@ async def delete( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._delete( - f"/v1/conversations/{conversation_id}", + path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -753,7 +753,7 @@ async def cancel( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/v1/conversations/{conversation_id}/cancel", + path_template("/v1/conversations/{conversation_id}/cancel", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -809,7 +809,7 @@ async def recompile( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/v1/conversations/{conversation_id}/recompile", + path_template("/v1/conversations/{conversation_id}/recompile", conversation_id=conversation_id), body=await async_maybe_transform( { "agent_id": agent_id, diff --git a/src/letta_client/resources/conversations/messages.py b/src/letta_client/resources/conversations/messages.py index e3070101..41a8551c 100644 --- a/src/letta_client/resources/conversations/messages.py +++ b/src/letta_client/resources/conversations/messages.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -180,7 +180,7 @@ def create( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/v1/conversations/{conversation_id}/messages", + path_template("/v1/conversations/{conversation_id}/messages", conversation_id=conversation_id), body=maybe_transform( { "agent_id": agent_id, @@ -281,7 +281,7 @@ def list( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._get_api_list( - f"/v1/conversations/{conversation_id}/messages", + path_template("/v1/conversations/{conversation_id}/messages", conversation_id=conversation_id), page=SyncArrayPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -354,7 +354,7 @@ def compact( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/v1/conversations/{conversation_id}/compact", + path_template("/v1/conversations/{conversation_id}/compact", conversation_id=conversation_id), body=maybe_transform( { "agent_id": agent_id, @@ -425,7 +425,7 @@ def stream( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/v1/conversations/{conversation_id}/stream", + path_template("/v1/conversations/{conversation_id}/stream", conversation_id=conversation_id), body=maybe_transform( { "agent_id": agent_id, @@ -590,7 +590,7 @@ async def create( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/v1/conversations/{conversation_id}/messages", + path_template("/v1/conversations/{conversation_id}/messages", conversation_id=conversation_id), body=await async_maybe_transform( { "agent_id": agent_id, @@ -691,7 +691,7 @@ def list( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._get_api_list( - f"/v1/conversations/{conversation_id}/messages", + path_template("/v1/conversations/{conversation_id}/messages", conversation_id=conversation_id), page=AsyncArrayPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -764,7 +764,7 @@ async def compact( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/v1/conversations/{conversation_id}/compact", + path_template("/v1/conversations/{conversation_id}/compact", conversation_id=conversation_id), body=await async_maybe_transform( { "agent_id": agent_id, @@ -835,7 +835,7 @@ async def stream( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/v1/conversations/{conversation_id}/stream", + path_template("/v1/conversations/{conversation_id}/stream", conversation_id=conversation_id), body=await async_maybe_transform( { "agent_id": agent_id, diff --git a/src/letta_client/resources/folders/agents.py b/src/letta_client/resources/folders/agents.py index 1ef8b118..b0cf05e4 100644 --- a/src/letta_client/resources/folders/agents.py +++ b/src/letta_client/resources/folders/agents.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -90,7 +90,7 @@ def list( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._get( - f"/v1/folders/{folder_id}/agents", + path_template("/v1/folders/{folder_id}/agents", folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -177,7 +177,7 @@ async def list( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return await self._get( - f"/v1/folders/{folder_id}/agents", + path_template("/v1/folders/{folder_id}/agents", folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/letta_client/resources/folders/files.py b/src/letta_client/resources/folders/files.py index 09c9a7fb..de176d1a 100644 --- a/src/letta_client/resources/folders/files.py +++ b/src/letta_client/resources/folders/files.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given -from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -83,7 +83,7 @@ def retrieve( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( - f"/v1/folders/{folder_id}/files/{file_id}", + path_template("/v1/folders/{folder_id}/files/{file_id}", folder_id=folder_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -143,7 +143,7 @@ def list( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._get_api_list( - f"/v1/folders/{folder_id}/files", + path_template("/v1/folders/{folder_id}/files", folder_id=folder_id), page=SyncArrayPage[FileListResponse], options=make_request_options( extra_headers=extra_headers, @@ -199,7 +199,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/v1/folders/{folder_id}/{file_id}", + path_template("/v1/folders/{folder_id}/{file_id}", folder_id=folder_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -247,7 +247,7 @@ def upload( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( - f"/v1/folders/{folder_id}/upload", + path_template("/v1/folders/{folder_id}/upload", folder_id=folder_id), body=maybe_transform(body, file_upload_params.FileUploadParams), files=files, options=make_request_options( @@ -323,7 +323,7 @@ async def retrieve( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( - f"/v1/folders/{folder_id}/files/{file_id}", + path_template("/v1/folders/{folder_id}/files/{file_id}", folder_id=folder_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -385,7 +385,7 @@ def list( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._get_api_list( - f"/v1/folders/{folder_id}/files", + path_template("/v1/folders/{folder_id}/files", folder_id=folder_id), page=AsyncArrayPage[FileListResponse], options=make_request_options( extra_headers=extra_headers, @@ -441,7 +441,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/v1/folders/{folder_id}/{file_id}", + path_template("/v1/folders/{folder_id}/{file_id}", folder_id=folder_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -489,7 +489,7 @@ async def upload( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( - f"/v1/folders/{folder_id}/upload", + path_template("/v1/folders/{folder_id}/upload", folder_id=folder_id), body=await async_maybe_transform(body, file_upload_params.FileUploadParams), files=files, options=make_request_options( diff --git a/src/letta_client/resources/folders/folders.py b/src/letta_client/resources/folders/folders.py index 2f91cddd..27b92922 100644 --- a/src/letta_client/resources/folders/folders.py +++ b/src/letta_client/resources/folders/folders.py @@ -25,7 +25,7 @@ ) from ...types import folder_list_params, folder_create_params, folder_update_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -161,7 +161,7 @@ def retrieve( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._get( - f"/v1/folders/{folder_id}", + path_template("/v1/folders/{folder_id}", folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -211,7 +211,7 @@ def update( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._patch( - f"/v1/folders/{folder_id}", + path_template("/v1/folders/{folder_id}", folder_id=folder_id), body=maybe_transform( { "description": description, @@ -322,7 +322,7 @@ def delete( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return self._delete( - f"/v1/folders/{folder_id}", + path_template("/v1/folders/{folder_id}", folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -449,7 +449,7 @@ async def retrieve( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return await self._get( - f"/v1/folders/{folder_id}", + path_template("/v1/folders/{folder_id}", folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -499,7 +499,7 @@ async def update( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return await self._patch( - f"/v1/folders/{folder_id}", + path_template("/v1/folders/{folder_id}", folder_id=folder_id), body=await async_maybe_transform( { "description": description, @@ -610,7 +610,7 @@ async def delete( if not folder_id: raise ValueError(f"Expected a non-empty value for `folder_id` but received {folder_id!r}") return await self._delete( - f"/v1/folders/{folder_id}", + path_template("/v1/folders/{folder_id}", folder_id=folder_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/mcp_servers/mcp_servers.py b/src/letta_client/resources/mcp_servers/mcp_servers.py index 8a423dcf..9d6f697a 100644 --- a/src/letta_client/resources/mcp_servers/mcp_servers.py +++ b/src/letta_client/resources/mcp_servers/mcp_servers.py @@ -16,7 +16,7 @@ ) from ...types import mcp_server_create_params, mcp_server_update_params, mcp_server_refresh_params from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -134,7 +134,7 @@ def retrieve( return cast( McpServerRetrieveResponse, self._get( - f"/v1/mcp-servers/{mcp_server_id}", + path_template("/v1/mcp-servers/{mcp_server_id}", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -178,7 +178,7 @@ def update( return cast( McpServerUpdateResponse, self._patch( - f"/v1/mcp-servers/{mcp_server_id}", + path_template("/v1/mcp-servers/{mcp_server_id}", mcp_server_id=mcp_server_id), body=maybe_transform( { "config": config, @@ -241,7 +241,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `mcp_server_id` but received {mcp_server_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/v1/mcp-servers/{mcp_server_id}", + path_template("/v1/mcp-servers/{mcp_server_id}", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -283,7 +283,7 @@ def refresh( if not mcp_server_id: raise ValueError(f"Expected a non-empty value for `mcp_server_id` but received {mcp_server_id!r}") return self._patch( - f"/v1/mcp-servers/{mcp_server_id}/refresh", + path_template("/v1/mcp-servers/{mcp_server_id}/refresh", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -395,7 +395,7 @@ async def retrieve( return cast( McpServerRetrieveResponse, await self._get( - f"/v1/mcp-servers/{mcp_server_id}", + path_template("/v1/mcp-servers/{mcp_server_id}", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -439,7 +439,7 @@ async def update( return cast( McpServerUpdateResponse, await self._patch( - f"/v1/mcp-servers/{mcp_server_id}", + path_template("/v1/mcp-servers/{mcp_server_id}", mcp_server_id=mcp_server_id), body=await async_maybe_transform( { "config": config, @@ -502,7 +502,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `mcp_server_id` but received {mcp_server_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/v1/mcp-servers/{mcp_server_id}", + path_template("/v1/mcp-servers/{mcp_server_id}", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -544,7 +544,7 @@ async def refresh( if not mcp_server_id: raise ValueError(f"Expected a non-empty value for `mcp_server_id` but received {mcp_server_id!r}") return await self._patch( - f"/v1/mcp-servers/{mcp_server_id}/refresh", + path_template("/v1/mcp-servers/{mcp_server_id}/refresh", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/letta_client/resources/mcp_servers/tools.py b/src/letta_client/resources/mcp_servers/tools.py index 2b0e4269..4e4a355b 100644 --- a/src/letta_client/resources/mcp_servers/tools.py +++ b/src/letta_client/resources/mcp_servers/tools.py @@ -7,7 +7,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -74,7 +74,9 @@ def retrieve( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._get( - f"/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}", + path_template( + "/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}", mcp_server_id=mcp_server_id, tool_id=tool_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -107,7 +109,7 @@ def list( if not mcp_server_id: raise ValueError(f"Expected a non-empty value for `mcp_server_id` but received {mcp_server_id!r}") return self._get( - f"/v1/mcp-servers/{mcp_server_id}/tools", + path_template("/v1/mcp-servers/{mcp_server_id}/tools", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -149,7 +151,9 @@ def run( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._post( - f"/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}/run", + path_template( + "/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}/run", mcp_server_id=mcp_server_id, tool_id=tool_id + ), body=maybe_transform({"args": args}, tool_run_params.ToolRunParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -207,7 +211,9 @@ async def retrieve( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._get( - f"/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}", + path_template( + "/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}", mcp_server_id=mcp_server_id, tool_id=tool_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -240,7 +246,7 @@ async def list( if not mcp_server_id: raise ValueError(f"Expected a non-empty value for `mcp_server_id` but received {mcp_server_id!r}") return await self._get( - f"/v1/mcp-servers/{mcp_server_id}/tools", + path_template("/v1/mcp-servers/{mcp_server_id}/tools", mcp_server_id=mcp_server_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -282,7 +288,9 @@ async def run( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._post( - f"/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}/run", + path_template( + "/v1/mcp-servers/{mcp_server_id}/tools/{tool_id}/run", mcp_server_id=mcp_server_id, tool_id=tool_id + ), body=await async_maybe_transform({"args": args}, tool_run_params.ToolRunParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/letta_client/resources/messages.py b/src/letta_client/resources/messages.py index f5b5103c..5540193c 100644 --- a/src/letta_client/resources/messages.py +++ b/src/letta_client/resources/messages.py @@ -10,7 +10,7 @@ from ..types import message_list_params, message_search_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -75,7 +75,7 @@ def retrieve( if not message_id: raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") return self._get( - f"/v1/messages/{message_id}", + path_template("/v1/messages/{message_id}", message_id=message_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -259,7 +259,7 @@ async def retrieve( if not message_id: raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") return await self._get( - f"/v1/messages/{message_id}", + path_template("/v1/messages/{message_id}", message_id=message_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/runs/messages.py b/src/letta_client/resources/runs/messages.py index 42765772..40328c97 100644 --- a/src/letta_client/resources/runs/messages.py +++ b/src/letta_client/resources/runs/messages.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -91,7 +91,7 @@ def list( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get_api_list( - f"/v1/runs/{run_id}/messages", + path_template("/v1/runs/{run_id}/messages", run_id=run_id), page=SyncArrayPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -156,7 +156,7 @@ def stream( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._post( - f"/v1/runs/{run_id}/stream", + path_template("/v1/runs/{run_id}/stream", run_id=run_id), body=maybe_transform( { "agent_id": agent_id, @@ -240,7 +240,7 @@ def list( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get_api_list( - f"/v1/runs/{run_id}/messages", + path_template("/v1/runs/{run_id}/messages", run_id=run_id), page=AsyncArrayPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -305,7 +305,7 @@ async def stream( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._post( - f"/v1/runs/{run_id}/stream", + path_template("/v1/runs/{run_id}/stream", run_id=run_id), body=await async_maybe_transform( { "agent_id": agent_id, diff --git a/src/letta_client/resources/runs/runs.py b/src/letta_client/resources/runs/runs.py index 6cb253b1..deb2ab9f 100644 --- a/src/letta_client/resources/runs/runs.py +++ b/src/letta_client/resources/runs/runs.py @@ -33,7 +33,7 @@ ) from ...types import StopReasonType, run_list_params from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from .messages import ( MessagesResource, AsyncMessagesResource, @@ -120,7 +120,7 @@ def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get( - f"/v1/runs/{run_id}", + path_template("/v1/runs/{run_id}", run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -286,7 +286,7 @@ async def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._get( - f"/v1/runs/{run_id}", + path_template("/v1/runs/{run_id}", run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/runs/steps.py b/src/letta_client/resources/runs/steps.py index a9f14807..ac3e0b5a 100644 --- a/src/letta_client/resources/runs/steps.py +++ b/src/letta_client/resources/runs/steps.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -87,7 +87,7 @@ def list( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get_api_list( - f"/v1/runs/{run_id}/steps", + path_template("/v1/runs/{run_id}/steps", run_id=run_id), page=SyncArrayPage[Step], options=make_request_options( extra_headers=extra_headers, @@ -171,7 +171,7 @@ def list( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get_api_list( - f"/v1/runs/{run_id}/steps", + path_template("/v1/runs/{run_id}/steps", run_id=run_id), page=AsyncArrayPage[Step], options=make_request_options( extra_headers=extra_headers, diff --git a/src/letta_client/resources/runs/trace.py b/src/letta_client/resources/runs/trace.py index 2fa2dbfa..3fc7178d 100644 --- a/src/letta_client/resources/runs/trace.py +++ b/src/letta_client/resources/runs/trace.py @@ -5,7 +5,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -79,7 +79,7 @@ def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get( - f"/v1/runs/{run_id}/trace", + path_template("/v1/runs/{run_id}/trace", run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -149,7 +149,7 @@ async def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._get( - f"/v1/runs/{run_id}/trace", + path_template("/v1/runs/{run_id}/trace", run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/letta_client/resources/runs/usage.py b/src/letta_client/resources/runs/usage.py index 5ad63d58..e1e4cf32 100644 --- a/src/letta_client/resources/runs/usage.py +++ b/src/letta_client/resources/runs/usage.py @@ -5,6 +5,7 @@ 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 ( @@ -65,7 +66,7 @@ def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get( - f"/v1/runs/{run_id}/usage", + path_template("/v1/runs/{run_id}/usage", run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -119,7 +120,7 @@ async def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._get( - f"/v1/runs/{run_id}/usage", + path_template("/v1/runs/{run_id}/usage", run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/steps/feedback.py b/src/letta_client/resources/steps/feedback.py index 062de56e..e8b28fa4 100644 --- a/src/letta_client/resources/steps/feedback.py +++ b/src/letta_client/resources/steps/feedback.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -78,7 +78,7 @@ def create( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return self._patch( - f"/v1/steps/{step_id}/feedback", + path_template("/v1/steps/{step_id}/feedback", step_id=step_id), body=maybe_transform( { "feedback": feedback, @@ -147,7 +147,7 @@ async def create( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return await self._patch( - f"/v1/steps/{step_id}/feedback", + path_template("/v1/steps/{step_id}/feedback", step_id=step_id), body=await async_maybe_transform( { "feedback": feedback, diff --git a/src/letta_client/resources/steps/messages.py b/src/letta_client/resources/steps/messages.py index 958886e5..27dd6e89 100644 --- a/src/letta_client/resources/steps/messages.py +++ b/src/letta_client/resources/steps/messages.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -91,7 +91,7 @@ def list( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return self._get_api_list( - f"/v1/steps/{step_id}/messages", + path_template("/v1/steps/{step_id}/messages", step_id=step_id), page=SyncArrayPage[MessageListResponse], options=make_request_options( extra_headers=extra_headers, @@ -179,7 +179,7 @@ def list( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return self._get_api_list( - f"/v1/steps/{step_id}/messages", + path_template("/v1/steps/{step_id}/messages", step_id=step_id), page=AsyncArrayPage[MessageListResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/letta_client/resources/steps/metrics.py b/src/letta_client/resources/steps/metrics.py index b117c70a..7de87e63 100644 --- a/src/letta_client/resources/steps/metrics.py +++ b/src/letta_client/resources/steps/metrics.py @@ -5,6 +5,7 @@ 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 ( @@ -67,7 +68,7 @@ def retrieve( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return self._get( - f"/v1/steps/{step_id}/metrics", + path_template("/v1/steps/{step_id}/metrics", step_id=step_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -123,7 +124,7 @@ async def retrieve( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return await self._get( - f"/v1/steps/{step_id}/metrics", + path_template("/v1/steps/{step_id}/metrics", step_id=step_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/steps/steps.py b/src/letta_client/resources/steps/steps.py index 9feab8dc..e7a4580d 100644 --- a/src/letta_client/resources/steps/steps.py +++ b/src/letta_client/resources/steps/steps.py @@ -25,7 +25,7 @@ AsyncMetricsResourceWithStreamingResponse, ) from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from .feedback import ( FeedbackResource, AsyncFeedbackResource, @@ -121,7 +121,7 @@ def retrieve( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return self._get( - f"/v1/steps/{step_id}", + path_template("/v1/steps/{step_id}", step_id=step_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -289,7 +289,7 @@ async def retrieve( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return await self._get( - f"/v1/steps/{step_id}", + path_template("/v1/steps/{step_id}", step_id=step_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/steps/trace.py b/src/letta_client/resources/steps/trace.py index 06fd1346..bc0aa296 100644 --- a/src/letta_client/resources/steps/trace.py +++ b/src/letta_client/resources/steps/trace.py @@ -7,6 +7,7 @@ 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 ( @@ -69,7 +70,7 @@ def retrieve( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return self._get( - f"/v1/steps/{step_id}/trace", + path_template("/v1/steps/{step_id}/trace", step_id=step_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -125,7 +126,7 @@ async def retrieve( if not step_id: raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") return await self._get( - f"/v1/steps/{step_id}/trace", + path_template("/v1/steps/{step_id}/trace", step_id=step_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/resources/templates/agents.py b/src/letta_client/resources/templates/agents.py index fa67ce93..025b1ff4 100644 --- a/src/letta_client/resources/templates/agents.py +++ b/src/letta_client/resources/templates/agents.py @@ -7,7 +7,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -89,7 +89,7 @@ def create( if not template_version: raise ValueError(f"Expected a non-empty value for `template_version` but received {template_version!r}") return self._post( - f"/v1/templates/{template_version}/agents", + path_template("/v1/templates/{template_version}/agents", template_version=template_version), body=maybe_transform( { "agent_name": agent_name, @@ -174,7 +174,7 @@ async def create( if not template_version: raise ValueError(f"Expected a non-empty value for `template_version` but received {template_version!r}") return await self._post( - f"/v1/templates/{template_version}/agents", + path_template("/v1/templates/{template_version}/agents", template_version=template_version), body=await async_maybe_transform( { "agent_name": agent_name, diff --git a/src/letta_client/resources/templates/templates.py b/src/letta_client/resources/templates/templates.py index dfab1aba..8b705f86 100644 --- a/src/letta_client/resources/templates/templates.py +++ b/src/letta_client/resources/templates/templates.py @@ -17,7 +17,7 @@ ) from ...types import template_save_params, template_create_params, template_update_params, template_rollback_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import required_args, maybe_transform, async_maybe_transform +from ..._utils import path_template, required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -202,7 +202,7 @@ def update( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return self._patch( - f"/v1/templates/{template_name}", + path_template("/v1/templates/{template_name}", template_name=template_name), body=maybe_transform( { "agent_file_json": agent_file_json, @@ -243,7 +243,7 @@ def delete( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return self._delete( - f"/v1/templates/{template_name}", + path_template("/v1/templates/{template_name}", template_name=template_name), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -282,7 +282,7 @@ def rollback( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return self._post( - f"/v1/templates/{template_name}/rollback", + path_template("/v1/templates/{template_name}/rollback", template_name=template_name), body=maybe_transform({"version": version}, template_rollback_params.TemplateRollbackParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -341,7 +341,7 @@ def save( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return self._post( - f"/v1/templates/{template_name}/save", + path_template("/v1/templates/{template_name}/save", template_name=template_name), body=maybe_transform( { "block_reconciliation_strategy": block_reconciliation_strategy, @@ -526,7 +526,7 @@ async def update( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return await self._patch( - f"/v1/templates/{template_name}", + path_template("/v1/templates/{template_name}", template_name=template_name), body=await async_maybe_transform( { "agent_file_json": agent_file_json, @@ -567,7 +567,7 @@ async def delete( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return await self._delete( - f"/v1/templates/{template_name}", + path_template("/v1/templates/{template_name}", template_name=template_name), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -606,7 +606,7 @@ async def rollback( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return await self._post( - f"/v1/templates/{template_name}/rollback", + path_template("/v1/templates/{template_name}/rollback", template_name=template_name), body=await async_maybe_transform({"version": version}, template_rollback_params.TemplateRollbackParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -665,7 +665,7 @@ async def save( if not template_name: raise ValueError(f"Expected a non-empty value for `template_name` but received {template_name!r}") return await self._post( - f"/v1/templates/{template_name}/save", + path_template("/v1/templates/{template_name}/save", template_name=template_name), body=await async_maybe_transform( { "block_reconciliation_strategy": block_reconciliation_strategy, diff --git a/src/letta_client/resources/tools.py b/src/letta_client/resources/tools.py index b5d5585d..e65f0bf4 100644 --- a/src/letta_client/resources/tools.py +++ b/src/letta_client/resources/tools.py @@ -13,7 +13,7 @@ from ..types import tool_list_params, tool_create_params, tool_search_params, tool_update_params, tool_upsert_params from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -161,7 +161,7 @@ def retrieve( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._get( - f"/v1/tools/{tool_id}", + path_template("/v1/tools/{tool_id}", tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -234,7 +234,7 @@ def update( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._patch( - f"/v1/tools/{tool_id}", + path_template("/v1/tools/{tool_id}", tool_id=tool_id), body=maybe_transform( { "args_json_schema": args_json_schema, @@ -376,7 +376,7 @@ def delete( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return self._delete( - f"/v1/tools/{tool_id}", + path_template("/v1/tools/{tool_id}", tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -951,7 +951,7 @@ async def retrieve( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._get( - f"/v1/tools/{tool_id}", + path_template("/v1/tools/{tool_id}", tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1024,7 +1024,7 @@ async def update( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._patch( - f"/v1/tools/{tool_id}", + path_template("/v1/tools/{tool_id}", tool_id=tool_id), body=await async_maybe_transform( { "args_json_schema": args_json_schema, @@ -1166,7 +1166,7 @@ async def delete( if not tool_id: raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}") return await self._delete( - f"/v1/tools/{tool_id}", + path_template("/v1/tools/{tool_id}", tool_id=tool_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/letta_client/types/agent_create_params.py b/src/letta_client/types/agent_create_params.py index 47c71e8b..c526668b 100644 --- a/src/letta_client/types/agent_create_params.py +++ b/src/letta_client/types/agent_create_params.py @@ -38,6 +38,9 @@ "AgentCreateParams", "CompactionSettings", "CompactionSettingsModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettingsReasoning", + "CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettings", "CompactionSettingsModelSettingsZaiModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettingsThinking", @@ -46,6 +49,9 @@ "CompactionSettingsModelSettingsChatGptoAuthModelSettings", "CompactionSettingsModelSettingsChatGptoAuthModelSettingsReasoning", "ModelSettings", + "ModelSettingsSgLangModelSettings", + "ModelSettingsSgLangModelSettingsReasoning", + "ModelSettingsSgLangModelSettingsResponseFormat", "ModelSettingsZaiModelSettings", "ModelSettingsZaiModelSettingsResponseFormat", "ModelSettingsZaiModelSettingsThinking", @@ -264,6 +270,51 @@ class AgentCreateParams(TypedDict, total=False): """The tools used by the agent.""" +class CompactionSettingsModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class CompactionSettingsModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: CompactionSettingsModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + CompactionSettingsModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -353,6 +404,7 @@ class CompactionSettingsModelSettingsChatGptoAuthModelSettings(TypedDict, total= CompactionSettingsModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + CompactionSettingsModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, @@ -409,6 +461,51 @@ class CompactionSettings(TypedDict, total=False): """ +class ModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +ModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class ModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: ModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[ModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + ModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -498,6 +595,7 @@ class ModelSettingsChatGptoAuthModelSettings(TypedDict, total=False): ModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + ModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/agent_state.py b/src/letta_client/types/agent_state.py index 4051e432..0235c606 100644 --- a/src/letta_client/types/agent_state.py +++ b/src/letta_client/types/agent_state.py @@ -47,6 +47,9 @@ "Source", "CompactionSettings", "CompactionSettingsModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettingsReasoning", + "CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettings", "CompactionSettingsModelSettingsZaiModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettingsThinking", @@ -58,6 +61,9 @@ "IdentityProperty", "ManagedGroup", "ModelSettings", + "ModelSettingsSgLangModelSettings", + "ModelSettingsSgLangModelSettingsReasoning", + "ModelSettingsSgLangModelSettingsResponseFormat", "ModelSettingsZaiModelSettings", "ModelSettingsZaiModelSettingsResponseFormat", "ModelSettingsZaiModelSettingsThinking", @@ -203,6 +209,52 @@ class Source(BaseModel): """The vector database provider used for this source's passages""" +class CompactionSettingsModelSettingsSgLangModelSettingsReasoning(BaseModel): + """The reasoning configuration for the model.""" + + reasoning_effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None + """The reasoning effort to use when generating text reasoning models""" + + +CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Annotated[ + Union[TextResponseFormat, JsonSchemaResponseFormat, JsonObjectResponseFormat, None], + PropertyInfo(discriminator="type"), +] + + +class CompactionSettingsModelSettingsSgLangModelSettings(BaseModel): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: Optional[int] = None + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: Optional[bool] = None + """Whether to enable parallel tool calling.""" + + provider_type: Optional[Literal["sglang"]] = None + """The type of the provider.""" + + reasoning: Optional[CompactionSettingsModelSettingsSgLangModelSettingsReasoning] = None + """The reasoning configuration for the model.""" + + response_format: Optional[CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat] = None + """The response format for the model.""" + + strict: Optional[bool] = None + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: Optional[float] = None + """The temperature of the model.""" + + tool_call_parser: Optional[str] = None + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + CompactionSettingsModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Annotated[ Union[TextResponseFormat, JsonSchemaResponseFormat, JsonObjectResponseFormat, None], PropertyInfo(discriminator="type"), @@ -295,6 +347,7 @@ class CompactionSettingsModelSettingsChatGptoAuthModelSettings(BaseModel): CompactionSettingsModelSettings: TypeAlias = Annotated[ Union[ OpenAIModelSettings, + CompactionSettingsModelSettingsSgLangModelSettings, AnthropicModelSettings, GoogleAIModelSettings, GoogleVertexModelSettings, @@ -448,6 +501,52 @@ class ManagedGroup(BaseModel): turns_counter: Optional[int] = None +class ModelSettingsSgLangModelSettingsReasoning(BaseModel): + """The reasoning configuration for the model.""" + + reasoning_effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None + """The reasoning effort to use when generating text reasoning models""" + + +ModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Annotated[ + Union[TextResponseFormat, JsonSchemaResponseFormat, JsonObjectResponseFormat, None], + PropertyInfo(discriminator="type"), +] + + +class ModelSettingsSgLangModelSettings(BaseModel): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: Optional[int] = None + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: Optional[bool] = None + """Whether to enable parallel tool calling.""" + + provider_type: Optional[Literal["sglang"]] = None + """The type of the provider.""" + + reasoning: Optional[ModelSettingsSgLangModelSettingsReasoning] = None + """The reasoning configuration for the model.""" + + response_format: Optional[ModelSettingsSgLangModelSettingsResponseFormat] = None + """The response format for the model.""" + + strict: Optional[bool] = None + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: Optional[float] = None + """The temperature of the model.""" + + tool_call_parser: Optional[str] = None + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + ModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Annotated[ Union[TextResponseFormat, JsonSchemaResponseFormat, JsonObjectResponseFormat, None], PropertyInfo(discriminator="type"), @@ -540,6 +639,7 @@ class ModelSettingsChatGptoAuthModelSettings(BaseModel): ModelSettings: TypeAlias = Annotated[ Union[ OpenAIModelSettings, + ModelSettingsSgLangModelSettings, AnthropicModelSettings, GoogleAIModelSettings, GoogleVertexModelSettings, diff --git a/src/letta_client/types/agent_update_params.py b/src/letta_client/types/agent_update_params.py index ca247565..a3d96852 100644 --- a/src/letta_client/types/agent_update_params.py +++ b/src/letta_client/types/agent_update_params.py @@ -38,6 +38,9 @@ "AgentUpdateParams", "CompactionSettings", "CompactionSettingsModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettingsReasoning", + "CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettings", "CompactionSettingsModelSettingsZaiModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettingsThinking", @@ -46,6 +49,9 @@ "CompactionSettingsModelSettingsChatGptoAuthModelSettings", "CompactionSettingsModelSettingsChatGptoAuthModelSettingsReasoning", "ModelSettings", + "ModelSettingsSgLangModelSettings", + "ModelSettingsSgLangModelSettingsReasoning", + "ModelSettingsSgLangModelSettingsResponseFormat", "ModelSettingsZaiModelSettings", "ModelSettingsZaiModelSettingsResponseFormat", "ModelSettingsZaiModelSettingsThinking", @@ -206,6 +212,51 @@ class AgentUpdateParams(TypedDict, total=False): """The tool rules governing the agent.""" +class CompactionSettingsModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class CompactionSettingsModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: CompactionSettingsModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + CompactionSettingsModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -295,6 +346,7 @@ class CompactionSettingsModelSettingsChatGptoAuthModelSettings(TypedDict, total= CompactionSettingsModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + CompactionSettingsModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, @@ -351,6 +403,51 @@ class CompactionSettings(TypedDict, total=False): """ +class ModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +ModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class ModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: ModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[ModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + ModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -440,6 +537,7 @@ class ModelSettingsChatGptoAuthModelSettings(TypedDict, total=False): ModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + ModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/agents/approval_create_param.py b/src/letta_client/types/agents/approval_create_param.py index 2c9e0267..2dd535a6 100644 --- a/src/letta_client/types/agents/approval_create_param.py +++ b/src/letta_client/types/agents/approval_create_param.py @@ -28,6 +28,9 @@ class ApprovalCreateParam(TypedDict, total=False): group_id: Optional[str] """The multi-agent group that the message was sent in""" + otid: Optional[str] + """The offline threading id associated with this message""" + reason: Optional[str] """An optional explanation for the provided approval status""" diff --git a/src/letta_client/types/agents/message_compact_params.py b/src/letta_client/types/agents/message_compact_params.py index 2a2e8b41..6a351a96 100644 --- a/src/letta_client/types/agents/message_compact_params.py +++ b/src/letta_client/types/agents/message_compact_params.py @@ -23,6 +23,9 @@ "MessageCompactParams", "CompactionSettings", "CompactionSettingsModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettingsReasoning", + "CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettings", "CompactionSettingsModelSettingsZaiModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettingsThinking", @@ -42,6 +45,51 @@ class MessageCompactParams(TypedDict, total=False): """ +class CompactionSettingsModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class CompactionSettingsModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: CompactionSettingsModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + CompactionSettingsModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -131,6 +179,7 @@ class CompactionSettingsModelSettingsChatGptoAuthModelSettings(TypedDict, total= CompactionSettingsModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + CompactionSettingsModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/agents/message_create_async_params.py b/src/letta_client/types/agents/message_create_async_params.py index 37d77f3e..6941b1df 100644 --- a/src/letta_client/types/agents/message_create_async_params.py +++ b/src/letta_client/types/agents/message_create_async_params.py @@ -209,6 +209,12 @@ class MessageToolReturnCreate(TypedDict, total=False): tool_returns: Required[Iterable[ToolReturnParam]] """List of tool returns from client-side execution""" + group_id: Optional[str] + """The multi-agent group that the message was sent in""" + + otid: Optional[str] + """The offline threading id associated with this message""" + type: Literal["tool_return"] """The message type to be created.""" diff --git a/src/letta_client/types/agents/message_create_params.py b/src/letta_client/types/agents/message_create_params.py index 559d5510..87fb2cbb 100644 --- a/src/letta_client/types/agents/message_create_params.py +++ b/src/letta_client/types/agents/message_create_params.py @@ -226,6 +226,12 @@ class MessageToolReturnCreate(TypedDict, total=False): tool_returns: Required[Iterable[ToolReturnParam]] """List of tool returns from client-side execution""" + group_id: Optional[str] + """The multi-agent group that the message was sent in""" + + otid: Optional[str] + """The offline threading id associated with this message""" + type: Literal["tool_return"] """The message type to be created.""" diff --git a/src/letta_client/types/agents/message_stream_params.py b/src/letta_client/types/agents/message_stream_params.py index 1a23c2d5..69c53974 100644 --- a/src/letta_client/types/agents/message_stream_params.py +++ b/src/letta_client/types/agents/message_stream_params.py @@ -230,6 +230,12 @@ class MessageToolReturnCreate(TypedDict, total=False): tool_returns: Required[Iterable[ToolReturnParam]] """List of tool returns from client-side execution""" + group_id: Optional[str] + """The multi-agent group that the message was sent in""" + + otid: Optional[str] + """The offline threading id associated with this message""" + type: Literal["tool_return"] """The message type to be created.""" diff --git a/src/letta_client/types/conversation.py b/src/letta_client/types/conversation.py index 84130ff0..e6ca26fb 100644 --- a/src/letta_client/types/conversation.py +++ b/src/letta_client/types/conversation.py @@ -25,6 +25,9 @@ __all__ = [ "Conversation", "ModelSettings", + "ModelSettingsSgLangModelSettings", + "ModelSettingsSgLangModelSettingsReasoning", + "ModelSettingsSgLangModelSettingsResponseFormat", "ModelSettingsZaiModelSettings", "ModelSettingsZaiModelSettingsResponseFormat", "ModelSettingsZaiModelSettingsThinking", @@ -34,6 +37,53 @@ "ModelSettingsChatGptoAuthModelSettingsReasoning", ] + +class ModelSettingsSgLangModelSettingsReasoning(BaseModel): + """The reasoning configuration for the model.""" + + reasoning_effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None + """The reasoning effort to use when generating text reasoning models""" + + +ModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Annotated[ + Union[TextResponseFormat, JsonSchemaResponseFormat, JsonObjectResponseFormat, None], + PropertyInfo(discriminator="type"), +] + + +class ModelSettingsSgLangModelSettings(BaseModel): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: Optional[int] = None + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: Optional[bool] = None + """Whether to enable parallel tool calling.""" + + provider_type: Optional[Literal["sglang"]] = None + """The type of the provider.""" + + reasoning: Optional[ModelSettingsSgLangModelSettingsReasoning] = None + """The reasoning configuration for the model.""" + + response_format: Optional[ModelSettingsSgLangModelSettingsResponseFormat] = None + """The response format for the model.""" + + strict: Optional[bool] = None + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: Optional[float] = None + """The temperature of the model.""" + + tool_call_parser: Optional[str] = None + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + ModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Annotated[ Union[TextResponseFormat, JsonSchemaResponseFormat, JsonObjectResponseFormat, None], PropertyInfo(discriminator="type"), @@ -126,6 +176,7 @@ class ModelSettingsChatGptoAuthModelSettings(BaseModel): ModelSettings: TypeAlias = Annotated[ Union[ OpenAIModelSettings, + ModelSettingsSgLangModelSettings, AnthropicModelSettings, GoogleAIModelSettings, GoogleVertexModelSettings, diff --git a/src/letta_client/types/conversation_create_params.py b/src/letta_client/types/conversation_create_params.py index 8406ab29..8fd697bb 100644 --- a/src/letta_client/types/conversation_create_params.py +++ b/src/letta_client/types/conversation_create_params.py @@ -23,6 +23,9 @@ __all__ = [ "ConversationCreateParams", "ModelSettings", + "ModelSettingsSgLangModelSettings", + "ModelSettingsSgLangModelSettingsReasoning", + "ModelSettingsSgLangModelSettingsResponseFormat", "ModelSettingsZaiModelSettings", "ModelSettingsZaiModelSettingsResponseFormat", "ModelSettingsZaiModelSettingsThinking", @@ -57,6 +60,51 @@ class ConversationCreateParams(TypedDict, total=False): """A summary of the conversation.""" +class ModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +ModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class ModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: ModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[ModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + ModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -146,6 +194,7 @@ class ModelSettingsChatGptoAuthModelSettings(TypedDict, total=False): ModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + ModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/conversation_recompile_params.py b/src/letta_client/types/conversation_recompile_params.py index aa2c31c8..e015acb3 100644 --- a/src/letta_client/types/conversation_recompile_params.py +++ b/src/letta_client/types/conversation_recompile_params.py @@ -23,6 +23,9 @@ "ConversationRecompileParams", "CompactionSettings", "CompactionSettingsModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettingsReasoning", + "CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettings", "CompactionSettingsModelSettingsZaiModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettingsThinking", @@ -51,6 +54,51 @@ class ConversationRecompileParams(TypedDict, total=False): """ +class CompactionSettingsModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class CompactionSettingsModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: CompactionSettingsModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + CompactionSettingsModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -140,6 +188,7 @@ class CompactionSettingsModelSettingsChatGptoAuthModelSettings(TypedDict, total= CompactionSettingsModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + CompactionSettingsModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/conversation_update_params.py b/src/letta_client/types/conversation_update_params.py index 964ea76a..6c2313e4 100644 --- a/src/letta_client/types/conversation_update_params.py +++ b/src/letta_client/types/conversation_update_params.py @@ -22,6 +22,9 @@ __all__ = [ "ConversationUpdateParams", "ModelSettings", + "ModelSettingsSgLangModelSettings", + "ModelSettingsSgLangModelSettingsReasoning", + "ModelSettingsSgLangModelSettingsResponseFormat", "ModelSettingsZaiModelSettings", "ModelSettingsZaiModelSettingsResponseFormat", "ModelSettingsZaiModelSettingsThinking", @@ -46,6 +49,51 @@ class ConversationUpdateParams(TypedDict, total=False): """A summary of the conversation.""" +class ModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +ModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class ModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: ModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[ModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + ModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -135,6 +183,7 @@ class ModelSettingsChatGptoAuthModelSettings(TypedDict, total=False): ModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + ModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/conversations/message_compact_params.py b/src/letta_client/types/conversations/message_compact_params.py index 155888c4..56b8062b 100644 --- a/src/letta_client/types/conversations/message_compact_params.py +++ b/src/letta_client/types/conversations/message_compact_params.py @@ -23,6 +23,9 @@ "MessageCompactParams", "CompactionSettings", "CompactionSettingsModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettings", + "CompactionSettingsModelSettingsSgLangModelSettingsReasoning", + "CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettings", "CompactionSettingsModelSettingsZaiModelSettingsResponseFormat", "CompactionSettingsModelSettingsZaiModelSettingsThinking", @@ -48,6 +51,51 @@ class MessageCompactParams(TypedDict, total=False): """ +class CompactionSettingsModelSettingsSgLangModelSettingsReasoning(TypedDict, total=False): + """The reasoning configuration for the model.""" + + reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] + """The reasoning effort to use when generating text reasoning models""" + + +CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat: TypeAlias = Union[ + TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam +] + + +class CompactionSettingsModelSettingsSgLangModelSettings(TypedDict, total=False): + """ + SGLang model configuration (OpenAI-compatible runtime with SGLang-specific parsing). + """ + + max_output_tokens: int + """The maximum number of tokens the model can generate.""" + + parallel_tool_calls: bool + """Whether to enable parallel tool calling.""" + + provider_type: Literal["sglang"] + """The type of the provider.""" + + reasoning: CompactionSettingsModelSettingsSgLangModelSettingsReasoning + """The reasoning configuration for the model.""" + + response_format: Optional[CompactionSettingsModelSettingsSgLangModelSettingsResponseFormat] + """The response format for the model.""" + + strict: bool + """Enable strict mode for tool calling. + + When true, tool outputs are guaranteed to match JSON schemas. + """ + + temperature: float + """The temperature of the model.""" + + tool_call_parser: Optional[str] + """SGLang tool call parser name (for example 'glm47', 'qwen25', or 'hermes').""" + + CompactionSettingsModelSettingsZaiModelSettingsResponseFormat: TypeAlias = Union[ TextResponseFormatParam, JsonSchemaResponseFormatParam, JsonObjectResponseFormatParam ] @@ -137,6 +185,7 @@ class CompactionSettingsModelSettingsChatGptoAuthModelSettings(TypedDict, total= CompactionSettingsModelSettings: TypeAlias = Union[ OpenAIModelSettingsParam, + CompactionSettingsModelSettingsSgLangModelSettings, AnthropicModelSettingsParam, GoogleAIModelSettingsParam, GoogleVertexModelSettingsParam, diff --git a/src/letta_client/types/conversations/message_create_params.py b/src/letta_client/types/conversations/message_create_params.py index 65530575..e49865fd 100644 --- a/src/letta_client/types/conversations/message_create_params.py +++ b/src/letta_client/types/conversations/message_create_params.py @@ -236,6 +236,12 @@ class MessageToolReturnCreate(TypedDict, total=False): tool_returns: Required[Iterable[ToolReturnParam]] """List of tool returns from client-side execution""" + group_id: Optional[str] + """The multi-agent group that the message was sent in""" + + otid: Optional[str] + """The offline threading id associated with this message""" + type: Literal["tool_return"] """The message type to be created.""" diff --git a/src/letta_client/types/llm_config.py b/src/letta_client/types/llm_config.py index 1c88ce6a..258aecc7 100644 --- a/src/letta_client/types/llm_config.py +++ b/src/letta_client/types/llm_config.py @@ -59,6 +59,7 @@ class LlmConfig(BaseModel): "deepseek", "xai", "zai", + "zai_coding", "baseten", "fireworks", "openrouter", @@ -172,6 +173,13 @@ class LlmConfig(BaseModel): tier: Optional[str] = None """The cost tier for the model (cloud only).""" + tool_call_parser: Optional[str] = None + """SGLang tool call parser name (e.g. + + 'glm47', 'qwen25', 'hermes'). Used by the SGLang native adapter to parse tool + calls from raw model output. + """ + top_logprobs: Optional[int] = None """Number of most likely tokens to return at each position (0-20). diff --git a/src/letta_client/types/llm_config_param.py b/src/letta_client/types/llm_config_param.py index 5306c40e..943b6d38 100644 --- a/src/letta_client/types/llm_config_param.py +++ b/src/letta_client/types/llm_config_param.py @@ -55,6 +55,7 @@ class LlmConfigParam(TypedDict, total=False): "deepseek", "xai", "zai", + "zai_coding", "baseten", "fireworks", "openrouter", @@ -169,6 +170,13 @@ class LlmConfigParam(TypedDict, total=False): tier: Optional[str] """The cost tier for the model (cloud only).""" + tool_call_parser: Optional[str] + """SGLang tool call parser name (e.g. + + 'glm47', 'qwen25', 'hermes'). Used by the SGLang native adapter to parse tool + calls from raw model output. + """ + top_logprobs: Optional[int] """Number of most likely tokens to return at each position (0-20). diff --git a/src/letta_client/types/model.py b/src/letta_client/types/model.py index c4dc3349..9fa624f4 100644 --- a/src/letta_client/types/model.py +++ b/src/letta_client/types/model.py @@ -58,6 +58,7 @@ class Model(BaseModel): "deepseek", "xai", "zai", + "zai_coding", "openrouter", "chatgpt_oauth", ] = FieldInfo(alias="model_endpoint_type") @@ -160,6 +161,13 @@ class Model(BaseModel): tier: Optional[str] = None """Deprecated: The cost tier for the model (cloud only).""" + tool_call_parser: Optional[str] = None + """SGLang tool call parser name (e.g. + + 'glm47', 'qwen25', 'hermes'). Used by the SGLang native adapter to parse tool + calls from raw model output. + """ + top_logprobs: Optional[int] = None """Number of most likely tokens to return at each position (0-20). diff --git a/src/letta_client/types/provider_type.py b/src/letta_client/types/provider_type.py index 2e0e170c..98e5919f 100644 --- a/src/letta_client/types/provider_type.py +++ b/src/letta_client/types/provider_type.py @@ -29,4 +29,5 @@ "openrouter", "xai", "zai", + "zai_coding", ] diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index ecd1a590..6dead28f 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -121,6 +121,7 @@ def test_method_create_with_all_params(self, client: Letta) -> None: "strict": True, "temperature": 0, "tier": "tier", + "tool_call_parser": "tool_call_parser", "top_logprobs": 0, "verbosity": "low", }, @@ -347,6 +348,7 @@ def test_method_update_with_all_params(self, client: Letta) -> None: "strict": True, "temperature": 0, "tier": "tier", + "tool_call_parser": "tool_call_parser", "top_logprobs": 0, "verbosity": "low", }, @@ -792,6 +794,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncLetta) -> "strict": True, "temperature": 0, "tier": "tier", + "tool_call_parser": "tool_call_parser", "top_logprobs": 0, "verbosity": "low", }, @@ -1018,6 +1021,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncLetta) -> "strict": True, "temperature": 0, "tier": "tier", + "tool_call_parser": "tool_call_parser", "top_logprobs": 0, "verbosity": "low", }, diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 00000000..186a9256 --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from letta_client._utils._path import path_template + + +@pytest.mark.parametrize( + "template, kwargs, expected", + [ + ("/v1/{id}", dict(id="abc"), "/v1/abc"), + ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), + ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), + ("/{w}/{w}", dict(w="echo"), "/echo/echo"), + ("/v1/static", {}, "/v1/static"), + ("", {}, ""), + ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), + ("/v1/{v}", dict(v=None), "/v1/null"), + ("/v1/{v}", dict(v=True), "/v1/true"), + ("/v1/{v}", dict(v=False), "/v1/false"), + ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok + ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok + ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok + ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok + ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine + ( + "/v1/{a}?query={b}", + dict(a="../../other/endpoint", b="a&bad=true"), + "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", + ), + ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), + ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), + ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), + ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input + # Query: slash and ? are safe, # is not + ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), + ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), + ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), + ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), + # Fragment: slash and ? are safe + ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), + ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), + # Path: slash, ? and # are all encoded + ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), + ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), + ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), + # same var encoded differently by component + ( + "/v1/{v}?q={v}#{v}", + dict(v="a/b?c#d"), + "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", + ), + ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection + ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection + ], +) +def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: + assert path_template(template, **kwargs) == expected + + +def test_missing_kwarg_raises_key_error() -> None: + with pytest.raises(KeyError, match="org_id"): + path_template("/v1/{org_id}") + + +@pytest.mark.parametrize( + "template, kwargs", + [ + ("{a}/path", dict(a=".")), + ("{a}/path", dict(a="..")), + ("/v1/{a}", dict(a=".")), + ("/v1/{a}", dict(a="..")), + ("/v1/{a}/path", dict(a=".")), + ("/v1/{a}/path", dict(a="..")), + ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." + ("/v1/{a}.", dict(a=".")), # var + static → ".." + ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." + ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text + ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/{v}?q=1", dict(v="..")), + ("/v1/{v}#frag", dict(v="..")), + ], +) +def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: + with pytest.raises(ValueError, match="dot-segment"): + path_template(template, **kwargs)