codeocean-mcp-server 0.7.1__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/CHANGELOG.md +8 -0
  2. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/PKG-INFO +7 -1
  3. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/README.md +6 -0
  4. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/pyproject.toml +4 -2
  5. codeocean_mcp_server-0.8.0/src/codeocean_mcp_server/logging_config.py +47 -0
  6. codeocean_mcp_server-0.8.0/src/codeocean_mcp_server/search.py +136 -0
  7. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/server.py +2 -0
  8. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/capsules.py +15 -21
  9. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/data_assets.py +15 -9
  10. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/bedrock_call.py +10 -2
  11. codeocean_mcp_server-0.8.0/tests/conftest.py +23 -0
  12. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/test_multi_tool_calling.py +2 -0
  13. codeocean_mcp_server-0.8.0/tests/test_search.py +275 -0
  14. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/test_tool_calling.py +1 -0
  15. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/uv.lock +86 -86
  16. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/.gitignore +0 -0
  17. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/.python-version +0 -0
  18. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/LICENSE +0 -0
  19. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/RELEASE.md +0 -0
  20. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/__init__.py +0 -0
  21. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/file_utils.py +0 -0
  22. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/models.py +0 -0
  23. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/__init__.py +0 -0
  24. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/computations.py +0 -0
  25. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/custom_metadata.py +0 -0
  26. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/bedrock_tools_converter.py +0 -0
  27. {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/mcp_client.py +0 -0
@@ -1,6 +1,14 @@
1
1
  CHANGELOG
2
2
  =========
3
3
 
4
+ ## 0.8.0 (2026-01-27)
5
+
6
+ - [#24](https://github.com/codeocean/codeocean-mcp-server/pull/24) feat: Token efficient search responses
7
+
8
+ ## 0.7.2 (2025-12-27)
9
+
10
+ - [#20](https://github.com/codeocean/codeocean-mcp-server/pull/20) feat: custom log formatting
11
+
4
12
  ## 0.7.1 (2024-12-23)
5
13
 
6
14
  - [#18](https://github.com/codeocean/codeocean-mcp-server/pull/18) chore: ruff format and dep update
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeocean-mcp-server
3
- Version: 0.7.1
3
+ Version: 0.8.0
4
4
  Summary: Code Ocean MCP Server
5
5
  Project-URL: Homepage, https://github.com/codeocean/codeocean-mcp-server
6
6
  Project-URL: Issues, https://github.com/codeocean/codeocean-mcp-server/issues
@@ -228,3 +228,9 @@ This will start a web server where you can:
228
228
  - View available tools and resources
229
229
  - Test tool calls interactively
230
230
  - See server logs and responses
231
+
232
+ ## Log Formatting (Optional)
233
+
234
+ The MCP server supports custom log formatting through the `LOG_FORMAT` environment variable. This allows you to control the format of log messages output by the server.
235
+ **Example Format Strings:** `"%(asctime)s %(levelname)s [%(name)s] %(message)s"`.
236
+ If `LOG_FORMAT` is not set, the server uses FastMCP's default logging configuration.
@@ -210,3 +210,9 @@ This will start a web server where you can:
210
210
  - View available tools and resources
211
211
  - Test tool calls interactively
212
212
  - See server logs and responses
213
+
214
+ ## Log Formatting (Optional)
215
+
216
+ The MCP server supports custom log formatting through the `LOG_FORMAT` environment variable. This allows you to control the format of log messages output by the server.
217
+ **Example Format Strings:** `"%(asctime)s %(levelname)s [%(name)s] %(message)s"`.
218
+ If `LOG_FORMAT` is not set, the server uses FastMCP's default logging configuration.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codeocean-mcp-server"
3
- version = "0.7.1"
3
+ version = "0.8.0"
4
4
  authors = [{ name = "Code Ocean", email = "dev@codeocean.com" }]
5
5
  description = "Code Ocean MCP Server"
6
6
  readme = "README.md"
@@ -18,7 +18,7 @@ license = "MIT"
18
18
 
19
19
  [dependency-groups]
20
20
  dev = [
21
- "boto3>=1.42.16",
21
+ "boto3>=1.42.17",
22
22
  "deepdiff>=8.6.1",
23
23
  "hatch>=1.16.2",
24
24
  "mcp-python-client>=0.1.9",
@@ -66,3 +66,5 @@ filterwarnings = [
66
66
  "ignore:datetime.datetime.utcnow() is deprecated:DeprecationWarning:botocore.auth",
67
67
  "ignore::DeprecationWarning:botocore.*",
68
68
  ]
69
+ markers = ["integration: requires external services (AWS/Bedrock)"]
70
+ asyncio_mode = "strict"
@@ -0,0 +1,47 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+
5
+
6
+ def configure_logging() -> None:
7
+ """Configure logging based on LOG_FORMAT environment variable.
8
+
9
+ If LOG_FORMAT is set, configures the root logger with a StreamHandler
10
+ using the specified format string. This must be called before FastMCP
11
+ initialization to ensure our configuration takes precedence.
12
+
13
+ If LOG_FORMAT is not set or is empty, does nothing and lets FastMCP
14
+ configure logging with its default settings.
15
+
16
+ Environment variables:
17
+ LOG_FORMAT: Python logging format string (optional)
18
+
19
+ Examples:
20
+ - "%(asctime)s agent %(levelname)s [%(name)s] %(message)s"
21
+ - "%(levelname)s: %(message)s"
22
+ - "[%(name)s] %(message)s"
23
+
24
+ Note:
25
+ Invalid format strings will cause errors when log records are formatted,
26
+ not during initialization. This typically results in ValueError, KeyError,
27
+ or AttributeError being raised when logging occurs.
28
+
29
+ """
30
+ log_format = os.getenv("LOG_FORMAT", "").strip()
31
+
32
+ # If LOG_FORMAT is not set or empty, do nothing
33
+ if not log_format:
34
+ return
35
+
36
+ # Create handler for stderr (same as FastMCP default)
37
+ handler = logging.StreamHandler(sys.stderr)
38
+
39
+ # Create formatter with the specified format string
40
+ # This will raise an error if the format string is invalid (fail fast)
41
+ formatter = logging.Formatter(log_format)
42
+ handler.setFormatter(formatter)
43
+
44
+ # Configure root logger
45
+ # This must be done before FastMCP calls logging.basicConfig()
46
+ logging.root.addHandler(handler)
47
+ logging.root.setLevel(logging.INFO)
@@ -0,0 +1,136 @@
1
+ import re
2
+ from typing import Any, ClassVar, Optional
3
+
4
+ from pydantic import BaseModel
5
+
6
+ # Constants
7
+ MAX_DESCRIPTION_LENGTH = 200
8
+ MAX_TAGS_COUNT = 10
9
+ TRUNCATION_SUFFIX = "...(more)"
10
+ TAGS_TRUNCATION_MARKER = "..more.."
11
+
12
+
13
+ def truncate_description(description: Optional[str], max_length: int = MAX_DESCRIPTION_LENGTH) -> Optional[str]:
14
+ """Truncate description with word-boundary-aware cutting."""
15
+ if not description:
16
+ return None
17
+
18
+ # Light whitespace normalization: collapse 3+ consecutive whitespace to single space.
19
+ normalized = re.sub(r"\s{3,}", " ", description).strip()
20
+ if not normalized:
21
+ return None
22
+
23
+ if len(normalized) <= max_length:
24
+ return normalized
25
+
26
+ truncate_at = max_length - len(TRUNCATION_SUFFIX)
27
+ if truncate_at <= 0:
28
+ return TRUNCATION_SUFFIX[:max_length]
29
+
30
+ last_space = normalized.rfind(" ", 0, truncate_at)
31
+ cut_at = last_space if last_space > max_length // 2 else truncate_at
32
+ return normalized[:cut_at].rstrip() + TRUNCATION_SUFFIX
33
+
34
+
35
+ def limit_tags(tags: Optional[list[str]], max_count: int = MAX_TAGS_COUNT) -> list[str]:
36
+ """Limit tags to maximum count with truncation marker."""
37
+ if not tags:
38
+ return []
39
+
40
+ if len(tags) <= max_count:
41
+ return list(tags)
42
+
43
+ return list(tags[: max_count - 1]) + [TAGS_TRUNCATION_MARKER]
44
+
45
+
46
+ class CompactCapsuleItem(BaseModel):
47
+ """Compact capsule/pipeline item (id kept, other fields shortened)."""
48
+
49
+ id: str
50
+ n: str
51
+ s: str
52
+ d: Optional[str] = None
53
+ t: list[str]
54
+
55
+
56
+ class CompactDataAssetItem(BaseModel):
57
+ """Compact data asset item (id kept, other fields shortened)."""
58
+
59
+ id: str
60
+ n: str
61
+ d: Optional[str] = None
62
+ t: list[str]
63
+
64
+
65
+ class SearchMeta(BaseModel):
66
+ """Base model providing search metadata fields."""
67
+
68
+ has_more: bool
69
+ next_token: Optional[str] = None
70
+ item_count: int
71
+ field_names: Optional[dict[str, str]] = None
72
+
73
+
74
+ class CapsuleSearchResults(SearchMeta):
75
+ """Compact results: {items: [{id, n, s, d, t}], has_more, next_token, total_count}.
76
+
77
+ Fields: id=id, n=name, s=slug, d=description (truncated), t=tags (limited).
78
+ Set include_field_names=true to add field_names with full labels.
79
+ Use get_capsule(id) if full details needed.
80
+ """
81
+
82
+ items: list[CompactCapsuleItem]
83
+ FIELD_NAMES: ClassVar[dict[str, str]] = {"id": "id", "n": "name", "s": "slug", "d": "description", "t": "tags"}
84
+
85
+ @classmethod
86
+ def from_sdk_results(cls, sdk_results: Any, include_field_names: bool = False) -> "CapsuleSearchResults":
87
+ """Convert SDK search results to compact format."""
88
+ items = [
89
+ CompactCapsuleItem(
90
+ id=c.id,
91
+ n=c.name,
92
+ s=c.slug,
93
+ d=truncate_description(c.description),
94
+ t=limit_tags(c.tags),
95
+ )
96
+ for c in sdk_results.results
97
+ ]
98
+ return cls(
99
+ items=items,
100
+ has_more=sdk_results.has_more,
101
+ next_token=getattr(sdk_results, "next_token", None),
102
+ item_count=len(items),
103
+ field_names=cls.FIELD_NAMES if include_field_names else None,
104
+ )
105
+
106
+
107
+ class DataAssetSearchResults(SearchMeta):
108
+ """Compact results: {items: [{id, n, d, t}], has_more, next_token, total_count}.
109
+
110
+ Fields: id=id, n=name, d=description (truncated), t=tags (limited).
111
+ Set include_field_names=true to add field_names with full labels.
112
+ Use get_data_asset(id) if full details needed.
113
+ """
114
+
115
+ items: list[CompactDataAssetItem]
116
+ FIELD_NAMES: ClassVar[dict[str, str]] = {"id": "id", "n": "name", "d": "description", "t": "tags"}
117
+
118
+ @classmethod
119
+ def from_sdk_results(cls, sdk_results: Any, include_field_names: bool = False) -> "DataAssetSearchResults":
120
+ """Convert SDK search results to compact format."""
121
+ items = [
122
+ CompactDataAssetItem(
123
+ id=d.id,
124
+ n=d.name,
125
+ d=truncate_description(d.description),
126
+ t=limit_tags(d.tags),
127
+ )
128
+ for d in sdk_results.results
129
+ ]
130
+ return cls(
131
+ items=items,
132
+ has_more=sdk_results.has_more,
133
+ next_token=getattr(sdk_results, "next_token", None),
134
+ item_count=len(items),
135
+ field_names=cls.FIELD_NAMES if include_field_names else None,
136
+ )
@@ -3,6 +3,7 @@ import os
3
3
  from codeocean import CodeOcean
4
4
  from mcp.server.fastmcp import FastMCP
5
5
 
6
+ from codeocean_mcp_server.logging_config import configure_logging
6
7
  from codeocean_mcp_server.tools import (
7
8
  capsules,
8
9
  computations,
@@ -13,6 +14,7 @@ from codeocean_mcp_server.tools import (
13
14
 
14
15
  def main():
15
16
  """Run the MCP server."""
17
+ configure_logging()
16
18
  domain = os.getenv("CODEOCEAN_DOMAIN")
17
19
  token = os.getenv("CODEOCEAN_TOKEN")
18
20
  if not domain or not token:
@@ -3,7 +3,6 @@ from codeocean.capsule import (
3
3
  AppPanel,
4
4
  Capsule,
5
5
  CapsuleSearchParams,
6
- CapsuleSearchResults,
7
6
  Computation,
8
7
  DataAssetAttachParams,
9
8
  DataAssetAttachResults,
@@ -11,6 +10,7 @@ from codeocean.capsule import (
11
10
  from mcp.server.fastmcp import FastMCP
12
11
 
13
12
  from codeocean_mcp_server.models import dataclass_to_pydantic
13
+ from codeocean_mcp_server.search import CapsuleSearchResults
14
14
 
15
15
  AppPanelModel = dataclass_to_pydantic(AppPanel)
16
16
  CapsuleSearchParamsModel = dataclass_to_pydantic(CapsuleSearchParams)
@@ -20,31 +20,25 @@ DataAssetAttachParamsModel = dataclass_to_pydantic(DataAssetAttachParams)
20
20
  def add_tools(mcp: FastMCP, client: CodeOcean):
21
21
  """Add capsule tools to the MCP server."""
22
22
 
23
- @mcp.tool(
24
- description=(
25
- str(client.capsules.search_capsules.__doc__) + "Use only for capsule searches. "
26
- "Provide only the minimal required parameters (e.g. limit=10); "
27
- "do not include optional params "
28
- "like sort_by or sort_order unless requested."
29
- )
30
- )
31
- def search_capsules(search_params: CapsuleSearchParamsModel) -> CapsuleSearchResults:
23
+ @mcp.tool(description=(str(client.capsules.search_capsules.__doc__) + " " + str(CapsuleSearchResults.__doc__)))
24
+ def search_capsules(
25
+ search_params: CapsuleSearchParamsModel,
26
+ include_field_names: bool = False,
27
+ ) -> CapsuleSearchResults:
32
28
  """Search for capsules matching specified criteria."""
33
29
  params = CapsuleSearchParams(**search_params.model_dump(exclude_none=True))
34
- return client.capsules.search_capsules(params)
30
+ results = client.capsules.search_capsules(params)
31
+ return CapsuleSearchResults.from_sdk_results(results, include_field_names)
35
32
 
36
- @mcp.tool(
37
- description=(
38
- str(client.capsules.search_pipelines.__doc__) + "Use only for pipeline searches. "
39
- "Provide only the minimal required parameters (e.g. limit=10); "
40
- "do not include optional params "
41
- "like sort_by or sort_order unless requested."
42
- )
43
- )
44
- def search_pipelines(search_params: CapsuleSearchParamsModel) -> CapsuleSearchResults:
33
+ @mcp.tool(description=(str(client.capsules.search_pipelines.__doc__) + " " + str(CapsuleSearchResults.__doc__)))
34
+ def search_pipelines(
35
+ search_params: CapsuleSearchParamsModel,
36
+ include_field_names: bool = False,
37
+ ) -> CapsuleSearchResults:
45
38
  """Search for pipelines matching specified criteria."""
46
39
  params = CapsuleSearchParams(**search_params.model_dump(exclude_none=True))
47
- return client.capsules.search_pipelines(params)
40
+ results = client.capsules.search_pipelines(params)
41
+ return CapsuleSearchResults.from_sdk_results(results, include_field_names)
48
42
 
49
43
  @mcp.tool(
50
44
  description=(
@@ -5,7 +5,6 @@ from codeocean.data_asset import (
5
5
  DataAsset,
6
6
  DataAssetParams,
7
7
  DataAssetSearchParams,
8
- DataAssetSearchResults,
9
8
  DataAssetUpdateParams,
10
9
  FileURLs,
11
10
  Folder,
@@ -14,6 +13,7 @@ from mcp.server.fastmcp import FastMCP
14
13
 
15
14
  from codeocean_mcp_server.file_utils import download_and_read_file
16
15
  from codeocean_mcp_server.models import dataclass_to_pydantic
16
+ from codeocean_mcp_server.search import DataAssetSearchResults
17
17
 
18
18
  DataAssetModel = dataclass_to_pydantic(DataAsset)
19
19
  DataAssetParamsModel = dataclass_to_pydantic(DataAssetParams)
@@ -25,17 +25,23 @@ def add_tools(mcp: FastMCP, client: CodeOcean):
25
25
  """Add data asset tools to the MCP server."""
26
26
 
27
27
  @mcp.tool(
28
- description=(
29
- str(client.data_assets.search_data_assets.__doc__)
30
- + "Search for data assets (external or internal). You may filter by "
31
- "fields such as `origin`, tags, or other criteria supported by the "
32
- "SDK."
33
- )
28
+ description=(str(client.data_assets.search_data_assets.__doc__) + " " + str(DataAssetSearchResults.__doc__))
34
29
  )
35
- def search_data_assets(search_params: DataAssetSearchParamsModel) -> DataAssetSearchResults:
30
+ def search_data_assets(
31
+ search_params: DataAssetSearchParamsModel,
32
+ include_field_names: bool = False,
33
+ ) -> DataAssetSearchResults:
36
34
  """Retrieve data assets matching search criteria for datasets."""
37
35
  params = DataAssetSearchParams(**search_params.model_dump(exclude_none=True))
38
- return client.data_assets.search_data_assets(params)
36
+ results = client.data_assets.search_data_assets(params)
37
+ return DataAssetSearchResults.from_sdk_results(results, include_field_names)
38
+
39
+ @mcp.tool(
40
+ description=("Get full details for a data asset by ID. Use after compact search to retrieve complete metadata.")
41
+ )
42
+ def get_data_asset(data_asset_id: str) -> DataAsset:
43
+ """Retrieve a data asset by its ID."""
44
+ return client.data_assets.get_data_asset(data_asset_id)
39
45
 
40
46
  @mcp.tool(
41
47
  description=(
@@ -7,7 +7,15 @@ from bedrock_tools_converter import convert_tool_format
7
7
 
8
8
  BEDROCK_MODEL = os.getenv("BEDROCK_MODEL") or "amazon.nova-pro-v1:0"
9
9
 
10
- client = boto3.client("bedrock-runtime")
10
+ _client = None
11
+
12
+
13
+ def _get_client():
14
+ """Lazily initialize the Bedrock client."""
15
+ global _client
16
+ if _client is None:
17
+ _client = boto3.client("bedrock-runtime")
18
+ return _client
11
19
 
12
20
 
13
21
  def call_bedrock(
@@ -26,4 +34,4 @@ def call_bedrock(
26
34
  if tools is not None:
27
35
  payload["toolConfig"] = convert_tool_format(tools, model)
28
36
 
29
- return client.converse(**payload)
37
+ return _get_client().converse(**payload)
@@ -0,0 +1,23 @@
1
+ """Pytest configuration for codeocean-mcp-server tests."""
2
+
3
+ import pytest
4
+
5
+
6
+ def pytest_addoption(parser):
7
+ """Add --integration flag to pytest."""
8
+ parser.addoption(
9
+ "--integration",
10
+ action="store_true",
11
+ default=False,
12
+ help="Run integration tests (requires AWS/Bedrock setup)",
13
+ )
14
+
15
+
16
+ def pytest_collection_modifyitems(config, items):
17
+ """Skip integration tests unless --integration flag is passed."""
18
+ if config.getoption("--integration"):
19
+ return
20
+ skip_integration = pytest.mark.skip(reason="need --integration flag to run")
21
+ for item in items:
22
+ if "integration" in item.keywords:
23
+ item.add_marker(skip_integration)
@@ -1,3 +1,4 @@
1
+ import pytest
1
2
  from bedrock_call import call_bedrock
2
3
  from deepdiff import DeepDiff
3
4
  from mcp_client import get_tools
@@ -35,6 +36,7 @@ def check_diff(expected_response: dict, response: dict) -> None:
35
36
  assert not diff, f"tool_usage diverges from expected:\n\n-----\n{diff!r}\n------"
36
37
 
37
38
 
39
+ @pytest.mark.integration
38
40
  def test_prompt_generates_correct_tool_usage_sequence(
39
41
  agentic_prompt_chain: dict = agentic_prompt_chain,
40
42
  ):
@@ -0,0 +1,275 @@
1
+ """Unit tests for search module."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+ from codeocean_mcp_server.search import (
7
+ TAGS_TRUNCATION_MARKER,
8
+ TRUNCATION_SUFFIX,
9
+ CapsuleSearchResults,
10
+ DataAssetSearchResults,
11
+ limit_tags,
12
+ truncate_description,
13
+ )
14
+
15
+
16
+ # Mock dataclasses for SDK results (private to avoid docstring lint rules)
17
+ @dataclass
18
+ class _MockCapsule:
19
+ id: str
20
+ name: str
21
+ slug: str
22
+ description: Optional[str]
23
+ tags: Optional[list[str]]
24
+
25
+
26
+ @dataclass
27
+ class _MockDataAsset:
28
+ id: str
29
+ name: str
30
+ description: Optional[str]
31
+ tags: Optional[list[str]]
32
+
33
+
34
+ @dataclass
35
+ class _MockCapsuleResults:
36
+ results: list[_MockCapsule]
37
+ has_more: bool
38
+ next_token: Optional[str] = None
39
+
40
+
41
+ @dataclass
42
+ class _MockDataAssetResults:
43
+ results: list[_MockDataAsset]
44
+ has_more: bool
45
+ next_token: Optional[str] = None
46
+
47
+
48
+ class TestTruncateDescription:
49
+ """Tests for truncate_description function."""
50
+
51
+ def test_none_input(self):
52
+ """None input returns None."""
53
+ assert truncate_description(None) is None
54
+
55
+ def test_empty_string(self):
56
+ """Empty string input returns None."""
57
+ assert truncate_description("") is None
58
+
59
+ def test_whitespace_only(self):
60
+ """Whitespace-only input returns None."""
61
+ assert truncate_description(" ") is None
62
+
63
+ def test_short_description(self):
64
+ """Under 200 chars returns unchanged text."""
65
+ text = "This is a short description."
66
+ assert truncate_description(text) == text
67
+
68
+ def test_exact_length(self):
69
+ """Exactly 200 chars returns unchanged."""
70
+ text = "a" * 200
71
+ assert truncate_description(text) == text
72
+
73
+ def test_whitespace_preserved_single(self):
74
+ """Single newlines and spaces are preserved."""
75
+ text = "Line one\nLine two"
76
+ assert truncate_description(text) == text
77
+
78
+ def test_whitespace_collapsed_multiple(self):
79
+ """3+ consecutive whitespace chars are collapsed."""
80
+ text = "Word one Word two" # 3 spaces
81
+ assert truncate_description(text) == "Word one Word two"
82
+
83
+ text_newlines = "Line one\n\n\nLine two" # 3 newlines
84
+ assert truncate_description(text_newlines) == "Line one Line two"
85
+
86
+ def test_truncation_at_word_boundary(self):
87
+ """Truncation occurs at word boundary when possible."""
88
+ # Create text that's over 200 chars with spaces
89
+ text = "word " * 50 # 250 chars
90
+ result = truncate_description(text)
91
+
92
+ assert result is not None
93
+ assert result.endswith(TRUNCATION_SUFFIX)
94
+ assert len(result) <= 200
95
+ # Should end at a word boundary (not mid-word)
96
+ without_suffix = result[: -len(TRUNCATION_SUFFIX)]
97
+ assert without_suffix.endswith("word")
98
+
99
+ def test_truncation_no_space(self):
100
+ """Long string without spaces hard-cuts."""
101
+ text = "a" * 250
102
+ result = truncate_description(text)
103
+
104
+ assert result is not None
105
+ assert result.endswith(TRUNCATION_SUFFIX)
106
+ assert len(result) == 200
107
+
108
+ def test_truncation_suffix_present(self):
109
+ """Over 200 chars returns text ending with truncation suffix."""
110
+ text = "This is a long description. " * 20
111
+ result = truncate_description(text)
112
+
113
+ assert result is not None
114
+ assert result.endswith(TRUNCATION_SUFFIX)
115
+
116
+ def test_unicode_handled(self):
117
+ """Unicode chars handled correctly."""
118
+ text = "Hello \u4e16\u754c! " * 30 # "Hello 世界! " repeated
119
+ result = truncate_description(text)
120
+
121
+ assert result is not None
122
+ # Should be valid UTF-8 string
123
+ result.encode("utf-8")
124
+
125
+ def test_custom_max_length(self):
126
+ """Custom max_length is respected."""
127
+ text = "a" * 100
128
+ result = truncate_description(text, max_length=50)
129
+
130
+ assert result is not None
131
+ assert len(result) == 50
132
+ assert result.endswith(TRUNCATION_SUFFIX)
133
+
134
+
135
+ class TestLimitTags:
136
+ """Tests for limit_tags function."""
137
+
138
+ def test_none_input(self):
139
+ """None returns empty list."""
140
+ assert limit_tags(None) == []
141
+
142
+ def test_empty_list(self):
143
+ """Empty list returns empty list."""
144
+ assert limit_tags([]) == []
145
+
146
+ def test_under_limit(self):
147
+ """Under 10 tags returns unchanged."""
148
+ tags = ["tag1", "tag2", "tag3"]
149
+ result = limit_tags(tags)
150
+ assert result == tags
151
+ # Should be a copy, not the same object
152
+ assert result is not tags
153
+
154
+ def test_exact_limit(self):
155
+ """Exactly 10 tags returns unchanged."""
156
+ tags = [f"tag{i}" for i in range(10)]
157
+ result = limit_tags(tags)
158
+ assert result == tags
159
+
160
+ def test_over_limit(self):
161
+ """Over 10 tags returns first 9 + marker."""
162
+ tags = [f"tag{i}" for i in range(15)]
163
+ result = limit_tags(tags)
164
+
165
+ assert len(result) == 10
166
+ assert result[:9] == tags[:9]
167
+ assert result[9] == TAGS_TRUNCATION_MARKER
168
+
169
+ def test_custom_max_count(self):
170
+ """Custom max_count is respected."""
171
+ tags = [f"tag{i}" for i in range(10)]
172
+ result = limit_tags(tags, max_count=5)
173
+
174
+ assert len(result) == 5
175
+ assert result[:4] == tags[:4]
176
+ assert result[4] == TAGS_TRUNCATION_MARKER
177
+
178
+
179
+ class TestCapsuleSearchResults:
180
+ """Tests for CapsuleSearchResults model."""
181
+
182
+ def test_from_sdk_results(self):
183
+ """from_sdk_results converts SDK results correctly."""
184
+ capsules = [
185
+ _MockCapsule(id="1", name="Capsule One", slug="capsule-one", description="First", tags=["a", "b"]),
186
+ _MockCapsule(id="2", name="Capsule Two", slug="capsule-two", description=None, tags=None),
187
+ ]
188
+ sdk_results = _MockCapsuleResults(results=capsules, has_more=True, next_token="token123")
189
+
190
+ result = CapsuleSearchResults.from_sdk_results(sdk_results, include_field_names=False)
191
+
192
+ assert len(result.items) == 2
193
+ assert result.items[0].id == "1"
194
+ assert result.items[0].n == "Capsule One"
195
+ assert result.items[0].s == "capsule-one"
196
+ assert result.items[0].d == "First"
197
+ assert result.items[0].t == ["a", "b"]
198
+ assert result.items[1].d is None
199
+ assert result.items[1].t == []
200
+ assert result.has_more is True
201
+ assert result.next_token == "token123"
202
+ assert result.item_count == 2
203
+ assert result.field_names is None
204
+
205
+ def test_from_sdk_results_with_field_names(self):
206
+ """from_sdk_results includes field_names when requested."""
207
+ sdk_results = _MockCapsuleResults(
208
+ results=[_MockCapsule(id="1", name="Test", slug="test", description=None, tags=None)],
209
+ has_more=False,
210
+ )
211
+
212
+ result = CapsuleSearchResults.from_sdk_results(sdk_results, include_field_names=True)
213
+
214
+ assert result.field_names == CapsuleSearchResults.FIELD_NAMES
215
+
216
+ def test_serializable(self):
217
+ """Result can be serialized to JSON."""
218
+ sdk_results = _MockCapsuleResults(
219
+ results=[_MockCapsule(id="1", name="Test", slug="test", description="Desc", tags=["t"])],
220
+ has_more=False,
221
+ )
222
+
223
+ result = CapsuleSearchResults.from_sdk_results(sdk_results)
224
+ json_str = result.model_dump_json()
225
+
226
+ assert "items" in json_str
227
+ assert "has_more" in json_str
228
+ assert "item_count" in json_str
229
+
230
+
231
+ class TestDataAssetSearchResults:
232
+ """Tests for DataAssetSearchResults model."""
233
+
234
+ def test_from_sdk_results(self):
235
+ """from_sdk_results converts SDK results correctly."""
236
+ assets = [
237
+ _MockDataAsset(id="da-1", name="Asset One", description="First asset", tags=["x"]),
238
+ _MockDataAsset(id="da-2", name="Asset Two", description=None, tags=None),
239
+ ]
240
+ sdk_results = _MockDataAssetResults(results=assets, has_more=False, next_token=None)
241
+
242
+ result = DataAssetSearchResults.from_sdk_results(sdk_results, include_field_names=False)
243
+
244
+ assert len(result.items) == 2
245
+ assert result.items[0].id == "da-1"
246
+ assert result.items[0].n == "Asset One"
247
+ assert result.items[0].d == "First asset"
248
+ assert result.items[0].t == ["x"]
249
+ assert result.items[1].d is None
250
+ assert result.items[1].t == []
251
+ assert result.has_more is False
252
+ assert result.next_token is None
253
+ assert result.item_count == 2
254
+ assert result.field_names is None
255
+
256
+ def test_truncation_applied(self):
257
+ """Long descriptions and many tags are truncated."""
258
+ asset = _MockDataAsset(
259
+ id="da-1",
260
+ name="Test",
261
+ description="x" * 300, # Long description
262
+ tags=[f"tag{i}" for i in range(20)], # Many tags
263
+ )
264
+ sdk_results = _MockDataAssetResults(results=[asset], has_more=False)
265
+
266
+ result = DataAssetSearchResults.from_sdk_results(sdk_results)
267
+
268
+ # Description should be truncated
269
+ assert result.items[0].d is not None
270
+ assert result.items[0].d.endswith(TRUNCATION_SUFFIX)
271
+ assert len(result.items[0].d) <= 200
272
+
273
+ # Tags should be limited with marker
274
+ assert len(result.items[0].t) == 10
275
+ assert result.items[0].t[-1] == TAGS_TRUNCATION_MARKER
@@ -438,6 +438,7 @@ test_cases = [(test["prompt"], test["expected"]) for test in test_response]
438
438
  ids = [test["id"] for test in test_response]
439
439
 
440
440
 
441
+ @pytest.mark.integration
441
442
  @pytest.mark.parametrize("prompt,expected_response", test_cases, ids=ids)
442
443
  def test_prompt_generating_the_right_tool_usage(prompt: str, expected_response: dict):
443
444
  """Test that the prompt generates the expected tool usage."""
@@ -323,30 +323,30 @@ wheels = [
323
323
 
324
324
  [[package]]
325
325
  name = "boto3"
326
- version = "1.42.16"
326
+ version = "1.42.17"
327
327
  source = { registry = "https://pypi.org/simple" }
328
328
  dependencies = [
329
329
  { name = "botocore" },
330
330
  { name = "jmespath" },
331
331
  { name = "s3transfer" },
332
332
  ]
333
- sdist = { url = "https://files.pythonhosted.org/packages/98/37/22c82e9d314d769a6eaf600ce5e08357927b5c6a614bfbeb1e7b7e7aa036/boto3-1.42.16.tar.gz", hash = "sha256:811391611db88c8a061f6e6fabbd7ca784ad9de04490a879f091cbaa9de7de74", size = 112834, upload-time = "2025-12-23T20:44:21.286Z" }
333
+ sdist = { url = "https://files.pythonhosted.org/packages/8d/cf/6e4a794e73cbf3e774ec27a5acc8442ce55d97cfc5226a20c1f58d8aee16/boto3-1.42.17.tar.gz", hash = "sha256:8a2e345e96d5ceba755c55539c93f99705f403fbfdeef2e838eabdc56750828b", size = 112776, upload-time = "2025-12-26T20:33:38.289Z" }
334
334
  wheels = [
335
- { url = "https://files.pythonhosted.org/packages/f8/93/78d175e7d40941c4b608a6701a14215aeeb1db58499dbbc40467a6fd6116/boto3-1.42.16-py3-none-any.whl", hash = "sha256:37a43d42aebd06a8f93ee801ea1b7b5181ac42a30869ef403c9dadc160a748e5", size = 140574, upload-time = "2025-12-23T20:44:20.121Z" },
335
+ { url = "https://files.pythonhosted.org/packages/6c/53/9f4241152eb1f5a57351232c14ef411ef35fbcc1d97a63873235d0503a8a/boto3-1.42.17-py3-none-any.whl", hash = "sha256:e0ee40f7102712452f6776af891c8f49b5ae9133bdaf22711d6f4a78963c2614", size = 140572, upload-time = "2025-12-26T20:33:36.307Z" },
336
336
  ]
337
337
 
338
338
  [[package]]
339
339
  name = "botocore"
340
- version = "1.42.16"
340
+ version = "1.42.17"
341
341
  source = { registry = "https://pypi.org/simple" }
342
342
  dependencies = [
343
343
  { name = "jmespath" },
344
344
  { name = "python-dateutil" },
345
345
  { name = "urllib3" },
346
346
  ]
347
- sdist = { url = "https://files.pythonhosted.org/packages/cf/eb/d91fb1fb288ba896392d68f89881f5f26bc5b51f8da28697c77f05bc44e8/botocore-1.42.16.tar.gz", hash = "sha256:29ee8555cd5d5023350405387cedcf3fe1c7f02fcb8060bf9e01602487482c25", size = 14914600, upload-time = "2025-12-23T20:44:11.025Z" }
347
+ sdist = { url = "https://files.pythonhosted.org/packages/4d/d2/128e2d8b9d426bd3a339e7dcf3eedca55f449af121604b6891ae80f6be9a/botocore-1.42.17.tar.gz", hash = "sha256:d73fe22c8e1497e4d59ff7dc68eb05afac68a4a6457656811562285d6132bc04", size = 14911757, upload-time = "2025-12-26T20:33:27.641Z" }
348
348
  wheels = [
349
- { url = "https://files.pythonhosted.org/packages/a2/98/c7c26ff399994e2b1119cc36027aaae46b9d646a49b70a82c2622e44c94b/botocore-1.42.16-py3-none-any.whl", hash = "sha256:b1f584a0f8645c12e07bf6ec9c18e05221a789f2a9b2d3c6291deb42f8c1c542", size = 14585775, upload-time = "2025-12-23T20:44:08.092Z" },
349
+ { url = "https://files.pythonhosted.org/packages/d1/47/cc00f2421f49784de8b9113c94b7b6580db989721f5109a24b9108308fe1/botocore-1.42.17-py3-none-any.whl", hash = "sha256:a832e4c04e63141221480967e9e511363aa54d24c405935fccb913a18583c96b", size = 14586536, upload-time = "2025-12-26T20:33:24.032Z" },
350
350
  ]
351
351
 
352
352
  [[package]]
@@ -529,7 +529,7 @@ wheels = [
529
529
 
530
530
  [[package]]
531
531
  name = "codeocean-mcp-server"
532
- version = "0.7.1"
532
+ version = "0.8.0"
533
533
  source = { editable = "." }
534
534
  dependencies = [
535
535
  { name = "codeocean" },
@@ -554,7 +554,7 @@ requires-dist = [
554
554
 
555
555
  [package.metadata.requires-dev]
556
556
  dev = [
557
- { name = "boto3", specifier = ">=1.42.16" },
557
+ { name = "boto3", specifier = ">=1.42.17" },
558
558
  { name = "deepdiff", specifier = ">=8.6.1" },
559
559
  { name = "hatch", specifier = ">=1.16.2" },
560
560
  { name = "mcp-python-client", specifier = ">=0.1.9" },
@@ -1184,14 +1184,14 @@ wheels = [
1184
1184
 
1185
1185
  [[package]]
1186
1186
  name = "jaraco-context"
1187
- version = "6.0.1"
1187
+ version = "6.0.2"
1188
1188
  source = { registry = "https://pypi.org/simple" }
1189
1189
  dependencies = [
1190
1190
  { name = "backports-tarfile", marker = "python_full_version < '3.12'" },
1191
1191
  ]
1192
- sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" }
1192
+ sdist = { url = "https://files.pythonhosted.org/packages/8d/7d/41acf8e22d791bde812cb6c2c36128bb932ed8ae066bcb5e39cb198e8253/jaraco_context-6.0.2.tar.gz", hash = "sha256:953ae8dddb57b1d791bf72ea1009b32088840a7dd19b9ba16443f62be919ee57", size = 14994, upload-time = "2025-12-24T19:21:35.784Z" }
1193
1193
  wheels = [
1194
- { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" },
1194
+ { url = "https://files.pythonhosted.org/packages/c7/0c/1e0096ced9c55f9c6c6655446798df74165780375d3f5ab5f33751e087ae/jaraco_context-6.0.2-py3-none-any.whl", hash = "sha256:55fc21af4b4f9ca94aa643b6ee7fe13b1e4c01abf3aeb98ca4ad9c80b741c786", size = 6988, upload-time = "2025-12-24T19:21:34.557Z" },
1195
1195
  ]
1196
1196
 
1197
1197
  [[package]]
@@ -1380,75 +1380,75 @@ wheels = [
1380
1380
 
1381
1381
  [[package]]
1382
1382
  name = "librt"
1383
- version = "0.7.4"
1384
- source = { registry = "https://pypi.org/simple" }
1385
- sdist = { url = "https://files.pythonhosted.org/packages/93/e4/b59bdf1197fdf9888452ea4d2048cdad61aef85eb83e99dc52551d7fdc04/librt-0.7.4.tar.gz", hash = "sha256:3871af56c59864d5fd21d1ac001eb2fb3b140d52ba0454720f2e4a19812404ba", size = 145862, upload-time = "2025-12-15T16:52:43.862Z" }
1386
- wheels = [
1387
- { url = "https://files.pythonhosted.org/packages/06/1e/3e61dff6c07a3b400fe907d3164b92b3b3023ef86eac1ee236869dc276f7/librt-0.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dc300cb5a5a01947b1ee8099233156fdccd5001739e5f596ecfbc0dab07b5a3b", size = 54708, upload-time = "2025-12-15T16:51:03.752Z" },
1388
- { url = "https://files.pythonhosted.org/packages/87/98/ab2428b0a80d0fd67decaeea84a5ec920e3dd4d95ecfd074c71f51bd7315/librt-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee8d3323d921e0f6919918a97f9b5445a7dfe647270b2629ec1008aa676c0bc0", size = 56656, upload-time = "2025-12-15T16:51:05.038Z" },
1389
- { url = "https://files.pythonhosted.org/packages/c1/ce/de1fad3a16e4fb5b6605bd6cbe6d0e5207cc8eca58993835749a1da0812b/librt-0.7.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:95cb80854a355b284c55f79674f6187cc9574df4dc362524e0cce98c89ee8331", size = 161024, upload-time = "2025-12-15T16:51:06.31Z" },
1390
- { url = "https://files.pythonhosted.org/packages/88/00/ddfcdc1147dd7fb68321d7b064b12f0b9101d85f466a46006f86096fde8d/librt-0.7.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca1caedf8331d8ad6027f93b52d68ed8f8009f5c420c246a46fe9d3be06be0f", size = 169529, upload-time = "2025-12-15T16:51:07.907Z" },
1391
- { url = "https://files.pythonhosted.org/packages/dd/b3/915702c7077df2483b015030d1979404474f490fe9a071e9576f7b26fef6/librt-0.7.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a6f1236151e6fe1da289351b5b5bce49651c91554ecc7b70a947bced6fe212", size = 183270, upload-time = "2025-12-15T16:51:09.164Z" },
1392
- { url = "https://files.pythonhosted.org/packages/45/19/ab2f217e8ec509fca4ea9e2e5022b9f72c1a7b7195f5a5770d299df807ea/librt-0.7.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7766b57aeebaf3f1dac14fdd4a75c9a61f2ed56d8ebeefe4189db1cb9d2a3783", size = 179038, upload-time = "2025-12-15T16:51:10.538Z" },
1393
- { url = "https://files.pythonhosted.org/packages/10/1c/d40851d187662cf50312ebbc0b277c7478dd78dbaaf5ee94056f1d7f2f83/librt-0.7.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1c4c89fb01157dd0a3bfe9e75cd6253b0a1678922befcd664eca0772a4c6c979", size = 173502, upload-time = "2025-12-15T16:51:11.888Z" },
1394
- { url = "https://files.pythonhosted.org/packages/07/52/d5880835c772b22c38db18660420fa6901fd9e9a433b65f0ba9b0f4da764/librt-0.7.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7fa8beef580091c02b4fd26542de046b2abfe0aaefa02e8bcf68acb7618f2b3", size = 193570, upload-time = "2025-12-15T16:51:13.168Z" },
1395
- { url = "https://files.pythonhosted.org/packages/f1/35/22d3c424b82f86ce019c0addadf001d459dfac8036aecc07fadc5c541053/librt-0.7.4-cp310-cp310-win32.whl", hash = "sha256:543c42fa242faae0466fe72d297976f3c710a357a219b1efde3a0539a68a6997", size = 42596, upload-time = "2025-12-15T16:51:14.422Z" },
1396
- { url = "https://files.pythonhosted.org/packages/95/b1/e7c316ac5fe60ac1fdfe515198087205220803c4cf923ee63e1cb8380b17/librt-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:25cc40d8eb63f0a7ea4c8f49f524989b9df901969cb860a2bc0e4bad4b8cb8a8", size = 48972, upload-time = "2025-12-15T16:51:15.516Z" },
1397
- { url = "https://files.pythonhosted.org/packages/84/64/44089b12d8b4714a7f0e2f33fb19285ba87702d4be0829f20b36ebeeee07/librt-0.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3485b9bb7dfa66167d5500ffdafdc35415b45f0da06c75eb7df131f3357b174a", size = 54709, upload-time = "2025-12-15T16:51:16.699Z" },
1398
- { url = "https://files.pythonhosted.org/packages/26/ef/6fa39fb5f37002f7d25e0da4f24d41b457582beea9369eeb7e9e73db5508/librt-0.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:188b4b1a770f7f95ea035d5bbb9d7367248fc9d12321deef78a269ebf46a5729", size = 56663, upload-time = "2025-12-15T16:51:17.856Z" },
1399
- { url = "https://files.pythonhosted.org/packages/9d/e4/cbaca170a13bee2469c90df9e47108610b4422c453aea1aec1779ac36c24/librt-0.7.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b668b1c840183e4e38ed5a99f62fac44c3a3eef16870f7f17cfdfb8b47550ed", size = 161703, upload-time = "2025-12-15T16:51:19.421Z" },
1400
- { url = "https://files.pythonhosted.org/packages/d0/32/0b2296f9cc7e693ab0d0835e355863512e5eac90450c412777bd699c76ae/librt-0.7.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e8f864b521f6cfedb314d171630f827efee08f5c3462bcbc2244ab8e1768cd6", size = 171027, upload-time = "2025-12-15T16:51:20.721Z" },
1401
- { url = "https://files.pythonhosted.org/packages/d8/33/c70b6d40f7342716e5f1353c8da92d9e32708a18cbfa44897a93ec2bf879/librt-0.7.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df7c9def4fc619a9c2ab402d73a0c5b53899abe090e0100323b13ccb5a3dd82", size = 184700, upload-time = "2025-12-15T16:51:22.272Z" },
1402
- { url = "https://files.pythonhosted.org/packages/e4/c8/555c405155da210e4c4113a879d378f54f850dbc7b794e847750a8fadd43/librt-0.7.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f79bc3595b6ed159a1bf0cdc70ed6ebec393a874565cab7088a219cca14da727", size = 180719, upload-time = "2025-12-15T16:51:23.561Z" },
1403
- { url = "https://files.pythonhosted.org/packages/6b/88/34dc1f1461c5613d1b73f0ecafc5316cc50adcc1b334435985b752ed53e5/librt-0.7.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77772a4b8b5f77d47d883846928c36d730b6e612a6388c74cba33ad9eb149c11", size = 174535, upload-time = "2025-12-15T16:51:25.031Z" },
1404
- { url = "https://files.pythonhosted.org/packages/b6/5a/f3fafe80a221626bcedfa9fe5abbf5f04070989d44782f579b2d5920d6d0/librt-0.7.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:064a286e6ab0b4c900e228ab4fa9cb3811b4b83d3e0cc5cd816b2d0f548cb61c", size = 195236, upload-time = "2025-12-15T16:51:26.328Z" },
1405
- { url = "https://files.pythonhosted.org/packages/d8/77/5c048d471ce17f4c3a6e08419be19add4d291e2f7067b877437d482622ac/librt-0.7.4-cp311-cp311-win32.whl", hash = "sha256:42da201c47c77b6cc91fc17e0e2b330154428d35d6024f3278aa2683e7e2daf2", size = 42930, upload-time = "2025-12-15T16:51:27.853Z" },
1406
- { url = "https://files.pythonhosted.org/packages/fb/3b/514a86305a12c3d9eac03e424b07cd312c7343a9f8a52719aa079590a552/librt-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:d31acb5886c16ae1711741f22504195af46edec8315fe69b77e477682a87a83e", size = 49240, upload-time = "2025-12-15T16:51:29.037Z" },
1407
- { url = "https://files.pythonhosted.org/packages/ba/01/3b7b1914f565926b780a734fac6e9a4d2c7aefe41f4e89357d73697a9457/librt-0.7.4-cp311-cp311-win_arm64.whl", hash = "sha256:114722f35093da080a333b3834fff04ef43147577ed99dd4db574b03a5f7d170", size = 42613, upload-time = "2025-12-15T16:51:30.194Z" },
1408
- { url = "https://files.pythonhosted.org/packages/f3/e7/b805d868d21f425b7e76a0ea71a2700290f2266a4f3c8357fcf73efc36aa/librt-0.7.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd3b5c37e0fb6666c27cf4e2c88ae43da904f2155c4cfc1e5a2fdce3b9fcf92", size = 55688, upload-time = "2025-12-15T16:51:31.571Z" },
1409
- { url = "https://files.pythonhosted.org/packages/59/5e/69a2b02e62a14cfd5bfd9f1e9adea294d5bcfeea219c7555730e5d068ee4/librt-0.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c5de1928c486201b23ed0cc4ac92e6e07be5cd7f3abc57c88a9cf4f0f32108", size = 57141, upload-time = "2025-12-15T16:51:32.714Z" },
1410
- { url = "https://files.pythonhosted.org/packages/6e/6b/05dba608aae1272b8ea5ff8ef12c47a4a099a04d1e00e28a94687261d403/librt-0.7.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078ae52ffb3f036396cc4aed558e5b61faedd504a3c1f62b8ae34bf95ae39d94", size = 165322, upload-time = "2025-12-15T16:51:33.986Z" },
1411
- { url = "https://files.pythonhosted.org/packages/8f/bc/199533d3fc04a4cda8d7776ee0d79955ab0c64c79ca079366fbc2617e680/librt-0.7.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce58420e25097b2fc201aef9b9f6d65df1eb8438e51154e1a7feb8847e4a55ab", size = 174216, upload-time = "2025-12-15T16:51:35.384Z" },
1412
- { url = "https://files.pythonhosted.org/packages/62/ec/09239b912a45a8ed117cb4a6616d9ff508f5d3131bd84329bf2f8d6564f1/librt-0.7.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b719c8730c02a606dc0e8413287e8e94ac2d32a51153b300baf1f62347858fba", size = 189005, upload-time = "2025-12-15T16:51:36.687Z" },
1413
- { url = "https://files.pythonhosted.org/packages/46/2e/e188313d54c02f5b0580dd31476bb4b0177514ff8d2be9f58d4a6dc3a7ba/librt-0.7.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3749ef74c170809e6dee68addec9d2458700a8de703de081c888e92a8b015cf9", size = 183960, upload-time = "2025-12-15T16:51:37.977Z" },
1414
- { url = "https://files.pythonhosted.org/packages/eb/84/f1d568d254518463d879161d3737b784137d236075215e56c7c9be191cee/librt-0.7.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b35c63f557653c05b5b1b6559a074dbabe0afee28ee2a05b6c9ba21ad0d16a74", size = 177609, upload-time = "2025-12-15T16:51:40.584Z" },
1415
- { url = "https://files.pythonhosted.org/packages/5d/43/060bbc1c002f0d757c33a1afe6bf6a565f947a04841139508fc7cef6c08b/librt-0.7.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1ef704e01cb6ad39ad7af668d51677557ca7e5d377663286f0ee1b6b27c28e5f", size = 199269, upload-time = "2025-12-15T16:51:41.879Z" },
1416
- { url = "https://files.pythonhosted.org/packages/ff/7f/708f8f02d8012ee9f366c07ea6a92882f48bd06cc1ff16a35e13d0fbfb08/librt-0.7.4-cp312-cp312-win32.whl", hash = "sha256:c66c2b245926ec15188aead25d395091cb5c9df008d3b3207268cd65557d6286", size = 43186, upload-time = "2025-12-15T16:51:43.149Z" },
1417
- { url = "https://files.pythonhosted.org/packages/f1/a5/4e051b061c8b2509be31b2c7ad4682090502c0a8b6406edcf8c6b4fe1ef7/librt-0.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:71a56f4671f7ff723451f26a6131754d7c1809e04e22ebfbac1db8c9e6767a20", size = 49455, upload-time = "2025-12-15T16:51:44.336Z" },
1418
- { url = "https://files.pythonhosted.org/packages/d0/d2/90d84e9f919224a3c1f393af1636d8638f54925fdc6cd5ee47f1548461e5/librt-0.7.4-cp312-cp312-win_arm64.whl", hash = "sha256:419eea245e7ec0fe664eb7e85e7ff97dcdb2513ca4f6b45a8ec4a3346904f95a", size = 42828, upload-time = "2025-12-15T16:51:45.498Z" },
1419
- { url = "https://files.pythonhosted.org/packages/fe/4d/46a53ccfbb39fd0b493fd4496eb76f3ebc15bb3e45d8c2e695a27587edf5/librt-0.7.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d44a1b1ba44cbd2fc3cb77992bef6d6fdb1028849824e1dd5e4d746e1f7f7f0b", size = 55745, upload-time = "2025-12-15T16:51:46.636Z" },
1420
- { url = "https://files.pythonhosted.org/packages/7f/2b/3ac7f5212b1828bf4f979cf87f547db948d3e28421d7a430d4db23346ce4/librt-0.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c9cab4b3de1f55e6c30a84c8cee20e4d3b2476f4d547256694a1b0163da4fe32", size = 57166, upload-time = "2025-12-15T16:51:48.219Z" },
1421
- { url = "https://files.pythonhosted.org/packages/e8/99/6523509097cbe25f363795f0c0d1c6a3746e30c2994e25b5aefdab119b21/librt-0.7.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2857c875f1edd1feef3c371fbf830a61b632fb4d1e57160bb1e6a3206e6abe67", size = 165833, upload-time = "2025-12-15T16:51:49.443Z" },
1422
- { url = "https://files.pythonhosted.org/packages/fe/35/323611e59f8fe032649b4fb7e77f746f96eb7588fcbb31af26bae9630571/librt-0.7.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b370a77be0a16e1ad0270822c12c21462dc40496e891d3b0caf1617c8cc57e20", size = 174818, upload-time = "2025-12-15T16:51:51.015Z" },
1423
- { url = "https://files.pythonhosted.org/packages/41/e6/40fb2bb21616c6e06b6a64022802228066e9a31618f493e03f6b9661548a/librt-0.7.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d05acd46b9a52087bfc50c59dfdf96a2c480a601e8898a44821c7fd676598f74", size = 189607, upload-time = "2025-12-15T16:51:52.671Z" },
1424
- { url = "https://files.pythonhosted.org/packages/32/48/1b47c7d5d28b775941e739ed2bfe564b091c49201b9503514d69e4ed96d7/librt-0.7.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70969229cb23d9c1a80e14225838d56e464dc71fa34c8342c954fc50e7516dee", size = 184585, upload-time = "2025-12-15T16:51:54.027Z" },
1425
- { url = "https://files.pythonhosted.org/packages/75/a6/ee135dfb5d3b54d5d9001dbe483806229c6beac3ee2ba1092582b7efeb1b/librt-0.7.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4450c354b89dbb266730893862dbff06006c9ed5b06b6016d529b2bf644fc681", size = 178249, upload-time = "2025-12-15T16:51:55.248Z" },
1426
- { url = "https://files.pythonhosted.org/packages/04/87/d5b84ec997338be26af982bcd6679be0c1db9a32faadab1cf4bb24f9e992/librt-0.7.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:adefe0d48ad35b90b6f361f6ff5a1bd95af80c17d18619c093c60a20e7a5b60c", size = 199851, upload-time = "2025-12-15T16:51:56.933Z" },
1427
- { url = "https://files.pythonhosted.org/packages/86/63/ba1333bf48306fe398e3392a7427ce527f81b0b79d0d91618c4610ce9d15/librt-0.7.4-cp313-cp313-win32.whl", hash = "sha256:21ea710e96c1e050635700695095962a22ea420d4b3755a25e4909f2172b4ff2", size = 43249, upload-time = "2025-12-15T16:51:58.498Z" },
1428
- { url = "https://files.pythonhosted.org/packages/f9/8a/de2c6df06cdfa9308c080e6b060fe192790b6a48a47320b215e860f0e98c/librt-0.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:772e18696cf5a64afee908662fbcb1f907460ddc851336ee3a848ef7684c8e1e", size = 49417, upload-time = "2025-12-15T16:51:59.618Z" },
1429
- { url = "https://files.pythonhosted.org/packages/31/66/8ee0949efc389691381ed686185e43536c20e7ad880c122dd1f31e65c658/librt-0.7.4-cp313-cp313-win_arm64.whl", hash = "sha256:52e34c6af84e12921748c8354aa6acf1912ca98ba60cdaa6920e34793f1a0788", size = 42824, upload-time = "2025-12-15T16:52:00.784Z" },
1430
- { url = "https://files.pythonhosted.org/packages/74/81/6921e65c8708eb6636bbf383aa77e6c7dad33a598ed3b50c313306a2da9d/librt-0.7.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4f1ee004942eaaed6e06c087d93ebc1c67e9a293e5f6b9b5da558df6bf23dc5d", size = 55191, upload-time = "2025-12-15T16:52:01.97Z" },
1431
- { url = "https://files.pythonhosted.org/packages/0d/d6/3eb864af8a8de8b39cc8dd2e9ded1823979a27795d72c4eea0afa8c26c9f/librt-0.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d854c6dc0f689bad7ed452d2a3ecff58029d80612d336a45b62c35e917f42d23", size = 56898, upload-time = "2025-12-15T16:52:03.356Z" },
1432
- { url = "https://files.pythonhosted.org/packages/49/bc/b1d4c0711fdf79646225d576faee8747b8528a6ec1ceb6accfd89ade7102/librt-0.7.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4f7339d9e445280f23d63dea842c0c77379c4a47471c538fc8feedab9d8d063", size = 163725, upload-time = "2025-12-15T16:52:04.572Z" },
1433
- { url = "https://files.pythonhosted.org/packages/2c/08/61c41cd8f0a6a41fc99ea78a2205b88187e45ba9800792410ed62f033584/librt-0.7.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39003fc73f925e684f8521b2dbf34f61a5deb8a20a15dcf53e0d823190ce8848", size = 172469, upload-time = "2025-12-15T16:52:05.863Z" },
1434
- { url = "https://files.pythonhosted.org/packages/8b/c7/4ee18b4d57f01444230bc18cf59103aeab8f8c0f45e84e0e540094df1df1/librt-0.7.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb15ee29d95875ad697d449fe6071b67f730f15a6961913a2b0205015ca0843", size = 186804, upload-time = "2025-12-15T16:52:07.192Z" },
1435
- { url = "https://files.pythonhosted.org/packages/a1/af/009e8ba3fbf830c936842da048eda1b34b99329f402e49d88fafff6525d1/librt-0.7.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02a69369862099e37d00765583052a99d6a68af7e19b887e1b78fee0146b755a", size = 181807, upload-time = "2025-12-15T16:52:08.554Z" },
1436
- { url = "https://files.pythonhosted.org/packages/85/26/51ae25f813656a8b117c27a974f25e8c1e90abcd5a791ac685bf5b489a1b/librt-0.7.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ec72342cc4d62f38b25a94e28b9efefce41839aecdecf5e9627473ed04b7be16", size = 175595, upload-time = "2025-12-15T16:52:10.186Z" },
1437
- { url = "https://files.pythonhosted.org/packages/48/93/36d6c71f830305f88996b15c8e017aa8d1e03e2e947b40b55bbf1a34cf24/librt-0.7.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:776dbb9bfa0fc5ce64234b446995d8d9f04badf64f544ca036bd6cff6f0732ce", size = 196504, upload-time = "2025-12-15T16:52:11.472Z" },
1438
- { url = "https://files.pythonhosted.org/packages/08/11/8299e70862bb9d704735bf132c6be09c17b00fbc7cda0429a9df222fdc1b/librt-0.7.4-cp314-cp314-win32.whl", hash = "sha256:0f8cac84196d0ffcadf8469d9ded4d4e3a8b1c666095c2a291e22bf58e1e8a9f", size = 39738, upload-time = "2025-12-15T16:52:12.962Z" },
1439
- { url = "https://files.pythonhosted.org/packages/54/d5/656b0126e4e0f8e2725cd2d2a1ec40f71f37f6f03f135a26b663c0e1a737/librt-0.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:037f5cb6fe5abe23f1dc058054d50e9699fcc90d0677eee4e4f74a8677636a1a", size = 45976, upload-time = "2025-12-15T16:52:14.441Z" },
1440
- { url = "https://files.pythonhosted.org/packages/60/86/465ff07b75c1067da8fa7f02913c4ead096ef106cfac97a977f763783bfb/librt-0.7.4-cp314-cp314-win_arm64.whl", hash = "sha256:a5deebb53d7a4d7e2e758a96befcd8edaaca0633ae71857995a0f16033289e44", size = 39073, upload-time = "2025-12-15T16:52:15.621Z" },
1441
- { url = "https://files.pythonhosted.org/packages/b3/a0/24941f85960774a80d4b3c2aec651d7d980466da8101cae89e8b032a3e21/librt-0.7.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b4c25312c7f4e6ab35ab16211bdf819e6e4eddcba3b2ea632fb51c9a2a97e105", size = 57369, upload-time = "2025-12-15T16:52:16.782Z" },
1442
- { url = "https://files.pythonhosted.org/packages/77/a0/ddb259cae86ab415786c1547d0fe1b40f04a7b089f564fd5c0242a3fafb2/librt-0.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:618b7459bb392bdf373f2327e477597fff8f9e6a1878fffc1b711c013d1b0da4", size = 59230, upload-time = "2025-12-15T16:52:18.259Z" },
1443
- { url = "https://files.pythonhosted.org/packages/31/11/77823cb530ab8a0c6fac848ac65b745be446f6f301753b8990e8809080c9/librt-0.7.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1437c3f72a30c7047f16fd3e972ea58b90172c3c6ca309645c1c68984f05526a", size = 183869, upload-time = "2025-12-15T16:52:19.457Z" },
1444
- { url = "https://files.pythonhosted.org/packages/a4/ce/157db3614cf3034b3f702ae5ba4fefda4686f11eea4b7b96542324a7a0e7/librt-0.7.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c96cb76f055b33308f6858b9b594618f1b46e147a4d03a4d7f0c449e304b9b95", size = 194606, upload-time = "2025-12-15T16:52:20.795Z" },
1445
- { url = "https://files.pythonhosted.org/packages/30/ef/6ec4c7e3d6490f69a4fd2803516fa5334a848a4173eac26d8ee6507bff6e/librt-0.7.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28f990e6821204f516d09dc39966ef8b84556ffd648d5926c9a3f681e8de8906", size = 206776, upload-time = "2025-12-15T16:52:22.229Z" },
1446
- { url = "https://files.pythonhosted.org/packages/ad/22/750b37bf549f60a4782ab80e9d1e9c44981374ab79a7ea68670159905918/librt-0.7.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc4aebecc79781a1b77d7d4e7d9fe080385a439e198d993b557b60f9117addaf", size = 203205, upload-time = "2025-12-15T16:52:23.603Z" },
1447
- { url = "https://files.pythonhosted.org/packages/7a/87/2e8a0f584412a93df5faad46c5fa0a6825fdb5eba2ce482074b114877f44/librt-0.7.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:022cc673e69283a42621dd453e2407cf1647e77f8bd857d7ad7499901e62376f", size = 196696, upload-time = "2025-12-15T16:52:24.951Z" },
1448
- { url = "https://files.pythonhosted.org/packages/e5/ca/7bf78fa950e43b564b7de52ceeb477fb211a11f5733227efa1591d05a307/librt-0.7.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b3ca211ae8ea540569e9c513da052699b7b06928dcda61247cb4f318122bdb5", size = 217191, upload-time = "2025-12-15T16:52:26.194Z" },
1449
- { url = "https://files.pythonhosted.org/packages/d6/49/3732b0e8424ae35ad5c3166d9dd5bcdae43ce98775e0867a716ff5868064/librt-0.7.4-cp314-cp314t-win32.whl", hash = "sha256:8a461f6456981d8c8e971ff5a55f2e34f4e60871e665d2f5fde23ee74dea4eeb", size = 40276, upload-time = "2025-12-15T16:52:27.54Z" },
1450
- { url = "https://files.pythonhosted.org/packages/35/d6/d8823e01bd069934525fddb343189c008b39828a429b473fb20d67d5cd36/librt-0.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:721a7b125a817d60bf4924e1eec2a7867bfcf64cfc333045de1df7a0629e4481", size = 46772, upload-time = "2025-12-15T16:52:28.653Z" },
1451
- { url = "https://files.pythonhosted.org/packages/36/e9/a0aa60f5322814dd084a89614e9e31139702e342f8459ad8af1984a18168/librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f", size = 39724, upload-time = "2025-12-15T16:52:29.836Z" },
1383
+ version = "0.7.5"
1384
+ source = { registry = "https://pypi.org/simple" }
1385
+ sdist = { url = "https://files.pythonhosted.org/packages/b5/8a/071f6628363d83e803d4783e0cd24fb9c5b798164300fcfaaa47c30659c0/librt-0.7.5.tar.gz", hash = "sha256:de4221a1181fa9c8c4b5f35506ed6f298948f44003d84d2a8b9885d7e01e6cfa", size = 145868, upload-time = "2025-12-25T03:53:16.039Z" }
1386
+ wheels = [
1387
+ { url = "https://files.pythonhosted.org/packages/88/f2/3248d8419db99ab80bb36266735d1241f766ad5fd993071211f789b618a5/librt-0.7.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81056e01bba1394f1d92904ec61a4078f66df785316275edbaf51d90da8c6e26", size = 54703, upload-time = "2025-12-25T03:51:48.394Z" },
1388
+ { url = "https://files.pythonhosted.org/packages/7b/30/7e179543dbcb1311f84b7e797658ad85cf2d4474c468f5dbafa13f2a98a5/librt-0.7.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7c72c8756eeb3aefb1b9e3dac7c37a4a25db63640cac0ab6fc18e91a0edf05a", size = 56660, upload-time = "2025-12-25T03:51:49.791Z" },
1389
+ { url = "https://files.pythonhosted.org/packages/15/91/3ba03ac1ac1abd66757a134b3bd56d9674928b163d0e686ea065a2bbb92d/librt-0.7.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddc4a16207f88f9597b397fc1f60781266d13b13de922ff61c206547a29e4bbd", size = 161026, upload-time = "2025-12-25T03:51:51.021Z" },
1390
+ { url = "https://files.pythonhosted.org/packages/0d/6e/b8365f547817d37b44c4be2ffa02630be995ef18be52d72698cecc3640c5/librt-0.7.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63055d3dda433ebb314c9f1819942f16a19203c454508fdb2d167613f7017169", size = 169530, upload-time = "2025-12-25T03:51:52.417Z" },
1391
+ { url = "https://files.pythonhosted.org/packages/63/6a/8442eb0b6933c651a06e1888f863971f3391cc11338fdaa6ab969f7d1eac/librt-0.7.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f85f9b5db87b0f52e53c68ad2a0c5a53e00afa439bd54a1723742a2b1021276", size = 183272, upload-time = "2025-12-25T03:51:53.713Z" },
1392
+ { url = "https://files.pythonhosted.org/packages/90/c4/b1166df6ef8e1f68d309f50bf69e8e750a5ea12fe7e2cf202c771ff359fc/librt-0.7.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c566a4672564c5d54d8ab65cdaae5a87ee14c1564c1a2ddc7a9f5811c750f023", size = 179040, upload-time = "2025-12-25T03:51:55.048Z" },
1393
+ { url = "https://files.pythonhosted.org/packages/fc/30/8f3fd9fd975b16c37832d6c248b976d2a0e33f155063781e064f249b37f1/librt-0.7.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fee15c2a190ef389f14928135c6fb2d25cd3fdb7887bfd9a7b444bbdc8c06b96", size = 173506, upload-time = "2025-12-25T03:51:56.407Z" },
1394
+ { url = "https://files.pythonhosted.org/packages/75/71/c3d4d5658f9849bf8e07ffba99f892d49a0c9a4001323ed610db72aedc82/librt-0.7.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:584cb3e605ec45ba350962cec853e17be0a25a772f21f09f1e422f7044ae2a7d", size = 193573, upload-time = "2025-12-25T03:51:57.949Z" },
1395
+ { url = "https://files.pythonhosted.org/packages/86/7c/c1c8a0116a2eed3d58c8946c589a8f9e1354b9b825cc92eba58bb15f6fb1/librt-0.7.5-cp310-cp310-win32.whl", hash = "sha256:9c08527055fbb03c641c15bbc5b79dd2942fb6a3bd8dabf141dd7e97eeea4904", size = 42603, upload-time = "2025-12-25T03:51:59.215Z" },
1396
+ { url = "https://files.pythonhosted.org/packages/1d/00/b52c77ca294247420020b829b70465c6e6f2b9d59ab21d8051aac20432da/librt-0.7.5-cp310-cp310-win_amd64.whl", hash = "sha256:dd810f2d39c526c42ea205e0addad5dc08ef853c625387806a29d07f9d150d9b", size = 48977, upload-time = "2025-12-25T03:52:00.519Z" },
1397
+ { url = "https://files.pythonhosted.org/packages/11/89/42b3ccb702a7e5f7a4cf2afc8a0a8f8c5e7d4b4d3a7c3de6357673dddddb/librt-0.7.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f952e1a78c480edee8fb43aa2bf2e84dcd46c917d44f8065b883079d3893e8fc", size = 54705, upload-time = "2025-12-25T03:52:01.433Z" },
1398
+ { url = "https://files.pythonhosted.org/packages/bb/90/c16970b509c3c448c365041d326eeef5aeb2abaed81eb3187b26a3cd13f8/librt-0.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75965c1f4efb7234ff52a58b729d245a21e87e4b6a26a0ec08052f02b16274e4", size = 56667, upload-time = "2025-12-25T03:52:02.391Z" },
1399
+ { url = "https://files.pythonhosted.org/packages/ac/2f/da4bdf6c190503f4663fbb781dfae5564a2b1c3f39a2da8e1ac7536ac7bd/librt-0.7.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:732e0aa0385b59a1b2545159e781c792cc58ce9c134249233a7c7250a44684c4", size = 161705, upload-time = "2025-12-25T03:52:03.395Z" },
1400
+ { url = "https://files.pythonhosted.org/packages/fb/88/c5da8e1f5f22b23d56e1fbd87266799dcf32828d47bf69fabc6f9673c6eb/librt-0.7.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdde31759bd8888f3ef0eebda80394a48961328a17c264dce8cc35f4b9cde35d", size = 171029, upload-time = "2025-12-25T03:52:04.798Z" },
1401
+ { url = "https://files.pythonhosted.org/packages/38/8a/8dfc00a6f1febc094ed9a55a448fc0b3a591b5dfd83be6cfd76d0910b1f0/librt-0.7.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3146d52465b3b6397d25d513f428cb421c18df65b7378667bb5f1e3cc45805", size = 184704, upload-time = "2025-12-25T03:52:05.887Z" },
1402
+ { url = "https://files.pythonhosted.org/packages/ad/57/65dec835ff235f431801064a3b41268f2f5ee0d224dc3bbf46d911af5c1a/librt-0.7.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29c8d2fae11d4379ea207ba7fc69d43237e42cf8a9f90ec6e05993687e6d648b", size = 180720, upload-time = "2025-12-25T03:52:06.925Z" },
1403
+ { url = "https://files.pythonhosted.org/packages/1e/27/92033d169bbcaa0d9a2dd476c179e5171ec22ed574b1b135a3c6104fb7d4/librt-0.7.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb41f04046b4f22b1e7ba5ef513402cd2e3477ec610e5f92d38fe2bba383d419", size = 174538, upload-time = "2025-12-25T03:52:08.075Z" },
1404
+ { url = "https://files.pythonhosted.org/packages/44/5c/0127098743575d5340624d8d4ec508d4d5ff0877dcee6f55f54bf03e5ed0/librt-0.7.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8bb7883c1e94ceb87c2bf81385266f032da09cd040e804cc002f2c9d6b842e2f", size = 195240, upload-time = "2025-12-25T03:52:09.427Z" },
1405
+ { url = "https://files.pythonhosted.org/packages/47/0f/be028c3e906a8ee6d29a42fd362e6d57d4143057f2bc0c454d489a0f898b/librt-0.7.5-cp311-cp311-win32.whl", hash = "sha256:84d4a6b9efd6124f728558a18e79e7cc5c5d4efc09b2b846c910de7e564f5bad", size = 42941, upload-time = "2025-12-25T03:52:10.527Z" },
1406
+ { url = "https://files.pythonhosted.org/packages/ac/3a/2f0ed57f4c3ae3c841780a95dfbea4cd811c6842d9ee66171ce1af606d25/librt-0.7.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab4b0d3bee6f6ff7017e18e576ac7e41a06697d8dea4b8f3ab9e0c8e1300c409", size = 49244, upload-time = "2025-12-25T03:52:11.832Z" },
1407
+ { url = "https://files.pythonhosted.org/packages/ee/7c/d7932aedfa5a87771f9e2799e7185ec3a322f4a1f4aa87c234159b75c8c8/librt-0.7.5-cp311-cp311-win_arm64.whl", hash = "sha256:730be847daad773a3c898943cf67fb9845a3961d06fb79672ceb0a8cd8624cfa", size = 42614, upload-time = "2025-12-25T03:52:12.745Z" },
1408
+ { url = "https://files.pythonhosted.org/packages/33/9d/cb0a296cee177c0fee7999ada1c1af7eee0e2191372058814a4ca6d2baf0/librt-0.7.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba1077c562a046208a2dc6366227b3eeae8f2c2ab4b41eaf4fd2fa28cece4203", size = 55689, upload-time = "2025-12-25T03:52:14.041Z" },
1409
+ { url = "https://files.pythonhosted.org/packages/79/5c/d7de4d4228b74c5b81a3fbada157754bb29f0e1f8c38229c669a7f90422a/librt-0.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:654fdc971c76348a73af5240d8e2529265b9a7ba6321e38dd5bae7b0d4ab3abe", size = 57142, upload-time = "2025-12-25T03:52:15.336Z" },
1410
+ { url = "https://files.pythonhosted.org/packages/e5/b2/5da779184aae369b69f4ae84225f63741662a0fe422e91616c533895d7a4/librt-0.7.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6b7b58913d475911f6f33e8082f19dd9b120c4f4a5c911d07e395d67b81c6982", size = 165323, upload-time = "2025-12-25T03:52:16.384Z" },
1411
+ { url = "https://files.pythonhosted.org/packages/5a/40/6d5abc15ab6cc70e04c4d201bb28baffff4cfb46ab950b8e90935b162d58/librt-0.7.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8e0fd344bad57026a8f4ccfaf406486c2fc991838050c2fef156170edc3b775", size = 174218, upload-time = "2025-12-25T03:52:17.518Z" },
1412
+ { url = "https://files.pythonhosted.org/packages/0d/d0/5239a8507e6117a3cb59ce0095bdd258bd2a93d8d4b819a506da06d8d645/librt-0.7.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46aa91813c267c3f60db75d56419b42c0c0b9748ec2c568a0e3588e543fb4233", size = 189007, upload-time = "2025-12-25T03:52:18.585Z" },
1413
+ { url = "https://files.pythonhosted.org/packages/1f/a4/8eed1166ffddbb01c25363e4c4e655f4bac298debe9e5a2dcfaf942438a1/librt-0.7.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ddc0ab9dbc5f9ceaf2bf7a367bf01f2697660e908f6534800e88f43590b271db", size = 183962, upload-time = "2025-12-25T03:52:19.723Z" },
1414
+ { url = "https://files.pythonhosted.org/packages/a1/83/260e60aab2f5ccba04579c5c46eb3b855e51196fde6e2bcf6742d89140a8/librt-0.7.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7a488908a470451338607650f1c064175094aedebf4a4fa37890682e30ce0b57", size = 177611, upload-time = "2025-12-25T03:52:21.18Z" },
1415
+ { url = "https://files.pythonhosted.org/packages/c4/36/6dcfed0df41e9695665462bab59af15b7ed2b9c668d85c7ebadd022cbb76/librt-0.7.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47fc52602ffc374e69bf1b76536dc99f7f6dd876bd786c8213eaa3598be030a", size = 199273, upload-time = "2025-12-25T03:52:22.25Z" },
1416
+ { url = "https://files.pythonhosted.org/packages/a6/b7/157149c8cffae6bc4293a52e0267860cee2398cb270798d94f1c8a69b9ae/librt-0.7.5-cp312-cp312-win32.whl", hash = "sha256:cda8b025875946ffff5a9a7590bf9acde3eb02cb6200f06a2d3e691ef3d9955b", size = 43191, upload-time = "2025-12-25T03:52:23.643Z" },
1417
+ { url = "https://files.pythonhosted.org/packages/f8/91/197dfeb8d3bdeb0a5344d0d8b3077f183ba5e76c03f158126f6072730998/librt-0.7.5-cp312-cp312-win_amd64.whl", hash = "sha256:b591c094afd0ffda820e931148c9e48dc31a556dc5b2b9b3cc552fa710d858e4", size = 49462, upload-time = "2025-12-25T03:52:24.637Z" },
1418
+ { url = "https://files.pythonhosted.org/packages/03/ea/052a79454cc52081dfaa9a1c4c10a529f7a6a6805b2fac5805fea5b25975/librt-0.7.5-cp312-cp312-win_arm64.whl", hash = "sha256:532ddc6a8a6ca341b1cd7f4d999043e4c71a212b26fe9fd2e7f1e8bb4e873544", size = 42830, upload-time = "2025-12-25T03:52:25.944Z" },
1419
+ { url = "https://files.pythonhosted.org/packages/9f/9a/8f61e16de0ff76590af893cfb5b1aa5fa8b13e5e54433d0809c7033f59ed/librt-0.7.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b1795c4b2789b458fa290059062c2f5a297ddb28c31e704d27e161386469691a", size = 55750, upload-time = "2025-12-25T03:52:26.975Z" },
1420
+ { url = "https://files.pythonhosted.org/packages/05/7c/a8a883804851a066f301e0bad22b462260b965d5c9e7fe3c5de04e6f91f8/librt-0.7.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2fcbf2e135c11f721193aa5f42ba112bb1046afafbffd407cbc81d8d735c74d0", size = 57170, upload-time = "2025-12-25T03:52:27.948Z" },
1421
+ { url = "https://files.pythonhosted.org/packages/d6/5d/b3b47facf5945be294cf8a835b03589f70ee0e791522f99ec6782ed738b3/librt-0.7.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c039bbf79a9a2498404d1ae7e29a6c175e63678d7a54013a97397c40aee026c5", size = 165834, upload-time = "2025-12-25T03:52:29.09Z" },
1422
+ { url = "https://files.pythonhosted.org/packages/b4/b6/b26910cd0a4e43e5d02aacaaea0db0d2a52e87660dca08293067ee05601a/librt-0.7.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3919c9407faeeee35430ae135e3a78acd4ecaaaa73767529e2c15ca1d73ba325", size = 174820, upload-time = "2025-12-25T03:52:30.463Z" },
1423
+ { url = "https://files.pythonhosted.org/packages/a5/a3/81feddd345d4c869b7a693135a462ae275f964fcbbe793d01ea56a84c2ee/librt-0.7.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26b46620e1e0e45af510d9848ea0915e7040605dd2ae94ebefb6c962cbb6f7ec", size = 189609, upload-time = "2025-12-25T03:52:31.492Z" },
1424
+ { url = "https://files.pythonhosted.org/packages/ce/a9/31310796ef4157d1d37648bf4a3b84555319f14cee3e9bad7bdd7bfd9a35/librt-0.7.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9bbb8facc5375476d392990dd6a71f97e4cb42e2ac66f32e860f6e47299d5e89", size = 184589, upload-time = "2025-12-25T03:52:32.59Z" },
1425
+ { url = "https://files.pythonhosted.org/packages/32/22/da3900544cb0ac6ab7a2857850158a0a093b86f92b264aa6c4a4f2355ff3/librt-0.7.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e9e9c988b5ffde7be02180f864cbd17c0b0c1231c235748912ab2afa05789c25", size = 178251, upload-time = "2025-12-25T03:52:33.745Z" },
1426
+ { url = "https://files.pythonhosted.org/packages/db/77/78e02609846e78b9b8c8e361753b3dbac9a07e6d5b567fe518de9e074ab0/librt-0.7.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:edf6b465306215b19dbe6c3fb63cf374a8f3e1ad77f3b4c16544b83033bbb67b", size = 199852, upload-time = "2025-12-25T03:52:34.826Z" },
1427
+ { url = "https://files.pythonhosted.org/packages/2a/25/05706f6b346429c951582f1b3561f4d5e1418d0d7ba1a0c181237cd77b3b/librt-0.7.5-cp313-cp313-win32.whl", hash = "sha256:060bde69c3604f694bd8ae21a780fe8be46bb3dbb863642e8dfc75c931ca8eee", size = 43250, upload-time = "2025-12-25T03:52:35.905Z" },
1428
+ { url = "https://files.pythonhosted.org/packages/d9/59/c38677278ac0b9ae1afc611382ef6c9ea87f52ad257bd3d8d65f0eacdc6a/librt-0.7.5-cp313-cp313-win_amd64.whl", hash = "sha256:a82d5a0ee43aeae2116d7292c77cc8038f4841830ade8aa922e098933b468b9e", size = 49421, upload-time = "2025-12-25T03:52:36.895Z" },
1429
+ { url = "https://files.pythonhosted.org/packages/c0/47/1d71113df4a81de5fdfbd3d7244e05d3d67e89f25455c3380ca50b92741e/librt-0.7.5-cp313-cp313-win_arm64.whl", hash = "sha256:3c98a8d0ac9e2a7cb8ff8c53e5d6e8d82bfb2839abf144fdeaaa832f2a12aa45", size = 42827, upload-time = "2025-12-25T03:52:37.856Z" },
1430
+ { url = "https://files.pythonhosted.org/packages/97/ae/8635b4efdc784220f1378be640d8b1a794332f7f6ea81bb4859bf9d18aa7/librt-0.7.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9937574e6d842f359b8585903d04f5b4ab62277a091a93e02058158074dc52f2", size = 55191, upload-time = "2025-12-25T03:52:38.839Z" },
1431
+ { url = "https://files.pythonhosted.org/packages/52/11/ed7ef6955dc2032af37db9b0b31cd5486a138aa792e1bb9e64f0f4950e27/librt-0.7.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5cd3afd71e9bc146203b6c8141921e738364158d4aa7cdb9a874e2505163770f", size = 56894, upload-time = "2025-12-25T03:52:39.805Z" },
1432
+ { url = "https://files.pythonhosted.org/packages/24/f1/02921d4a66a1b5dcd0493b89ce76e2762b98c459fe2ad04b67b2ea6fdd39/librt-0.7.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cffa3ef0af29687455161cb446eff059bf27607f95163d6a37e27bcb37180f6", size = 163726, upload-time = "2025-12-25T03:52:40.79Z" },
1433
+ { url = "https://files.pythonhosted.org/packages/65/87/27df46d2756fcb7a82fa7f6ca038a0c6064c3e93ba65b0b86fbf6a4f76a2/librt-0.7.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82f3f088482e2229387eadf8215c03f7726d56f69cce8c0c40f0795aebc9b361", size = 172470, upload-time = "2025-12-25T03:52:42.226Z" },
1434
+ { url = "https://files.pythonhosted.org/packages/9f/a9/e65a35e5d423639f4f3d8e17301ff13cc41c2ff97677fe9c361c26dbfbb7/librt-0.7.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7aa33153a5bb0bac783d2c57885889b1162823384e8313d47800a0e10d0070e", size = 186807, upload-time = "2025-12-25T03:52:43.688Z" },
1435
+ { url = "https://files.pythonhosted.org/packages/d7/b0/ac68aa582a996b1241773bd419823290c42a13dc9f494704a12a17ddd7b6/librt-0.7.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:265729b551a2dd329cc47b323a182fb7961af42abf21e913c9dd7d3331b2f3c2", size = 181810, upload-time = "2025-12-25T03:52:45.095Z" },
1436
+ { url = "https://files.pythonhosted.org/packages/e1/c1/03f6717677f20acd2d690813ec2bbe12a2de305f32c61479c53f7b9413bc/librt-0.7.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:168e04663e126416ba712114050f413ac306759a1791d87b7c11d4428ba75760", size = 175599, upload-time = "2025-12-25T03:52:46.177Z" },
1437
+ { url = "https://files.pythonhosted.org/packages/01/d7/f976ff4c07c59b69bb5eec7e5886d43243075bbef834428124b073471c86/librt-0.7.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:553dc58987d1d853adda8aeadf4db8e29749f0b11877afcc429a9ad892818ae2", size = 196506, upload-time = "2025-12-25T03:52:47.327Z" },
1438
+ { url = "https://files.pythonhosted.org/packages/b7/74/004f068b8888e61b454568b5479f88018fceb14e511ac0609cccee7dd227/librt-0.7.5-cp314-cp314-win32.whl", hash = "sha256:263f4fae9eba277513357c871275b18d14de93fd49bf5e43dc60a97b81ad5eb8", size = 39747, upload-time = "2025-12-25T03:52:48.437Z" },
1439
+ { url = "https://files.pythonhosted.org/packages/37/b1/ea3ec8fcf5f0a00df21f08972af77ad799604a306db58587308067d27af8/librt-0.7.5-cp314-cp314-win_amd64.whl", hash = "sha256:85f485b7471571e99fab4f44eeb327dc0e1f814ada575f3fa85e698417d8a54e", size = 45970, upload-time = "2025-12-25T03:52:49.389Z" },
1440
+ { url = "https://files.pythonhosted.org/packages/5d/30/5e3fb7ac4614a50fc67e6954926137d50ebc27f36419c9963a94f931f649/librt-0.7.5-cp314-cp314-win_arm64.whl", hash = "sha256:49c596cd18e90e58b7caa4d7ca7606049c1802125fcff96b8af73fa5c3870e4d", size = 39075, upload-time = "2025-12-25T03:52:50.395Z" },
1441
+ { url = "https://files.pythonhosted.org/packages/a4/7f/0af0a9306a06c2aabee3a790f5aa560c50ec0a486ab818a572dd3db6c851/librt-0.7.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:54d2aef0b0f5056f130981ad45081b278602ff3657fe16c88529f5058038e802", size = 57375, upload-time = "2025-12-25T03:52:51.439Z" },
1442
+ { url = "https://files.pythonhosted.org/packages/57/1f/c85e510baf6572a3d6ef40c742eacedc02973ed2acdb5dba2658751d9af8/librt-0.7.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0b4791202296ad51ac09a3ff58eb49d9da8e3a4009167a6d76ac418a974e5fd4", size = 59234, upload-time = "2025-12-25T03:52:52.687Z" },
1443
+ { url = "https://files.pythonhosted.org/packages/49/b1/bb6535e4250cd18b88d6b18257575a0239fa1609ebba925f55f51ae08e8e/librt-0.7.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e860909fea75baef941ee6436e0453612505883b9d0d87924d4fda27865b9a2", size = 183873, upload-time = "2025-12-25T03:52:53.705Z" },
1444
+ { url = "https://files.pythonhosted.org/packages/8e/49/ad4a138cca46cdaa7f0e15fa912ce3ccb4cc0d4090bfeb8ccc35766fa6d5/librt-0.7.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f02c4337bf271c4f06637f5ff254fad2238c0b8e32a3a480ebb2fc5e26f754a5", size = 194609, upload-time = "2025-12-25T03:52:54.884Z" },
1445
+ { url = "https://files.pythonhosted.org/packages/9c/2d/3b3cb933092d94bb2c1d3c9b503d8775f08d806588c19a91ee4d1495c2a8/librt-0.7.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7f51ffe59f4556243d3cc82d827bde74765f594fa3ceb80ec4de0c13ccd3416", size = 206777, upload-time = "2025-12-25T03:52:55.969Z" },
1446
+ { url = "https://files.pythonhosted.org/packages/3a/52/6e7611d3d1347812233dabc44abca4c8065ee97b83c9790d7ecc3f782bc8/librt-0.7.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0b7f080ba30601dfa3e3deed3160352273e1b9bc92e652f51103c3e9298f7899", size = 203208, upload-time = "2025-12-25T03:52:57.036Z" },
1447
+ { url = "https://files.pythonhosted.org/packages/27/aa/466ae4654bd2d45903fbf180815d41e3ae8903e5a1861f319f73c960a843/librt-0.7.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fb565b4219abc8ea2402e61c7ba648a62903831059ed3564fa1245cc245d58d7", size = 196698, upload-time = "2025-12-25T03:52:58.481Z" },
1448
+ { url = "https://files.pythonhosted.org/packages/97/8f/424f7e4525bb26fe0d3e984d1c0810ced95e53be4fd867ad5916776e18a3/librt-0.7.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a3cfb15961e7333ea6ef033dc574af75153b5c230d5ad25fbcd55198f21e0cf", size = 217194, upload-time = "2025-12-25T03:52:59.575Z" },
1449
+ { url = "https://files.pythonhosted.org/packages/9e/33/13a4cb798a171b173f3c94db23adaf13a417130e1493933dc0df0d7fb439/librt-0.7.5-cp314-cp314t-win32.whl", hash = "sha256:118716de5ad6726332db1801bc90fa6d94194cd2e07c1a7822cebf12c496714d", size = 40282, upload-time = "2025-12-25T03:53:01.091Z" },
1450
+ { url = "https://files.pythonhosted.org/packages/5f/f1/62b136301796399d65dad73b580f4509bcbd347dff885a450bff08e80cb6/librt-0.7.5-cp314-cp314t-win_amd64.whl", hash = "sha256:3dd58f7ce20360c6ce0c04f7bd9081c7f9c19fc6129a3c705d0c5a35439f201d", size = 46764, upload-time = "2025-12-25T03:53:02.381Z" },
1451
+ { url = "https://files.pythonhosted.org/packages/49/cb/940431d9410fda74f941f5cd7f0e5a22c63be7b0c10fa98b2b7022b48cb1/librt-0.7.5-cp314-cp314t-win_arm64.whl", hash = "sha256:08153ea537609d11f774d2bfe84af39d50d5c9ca3a4d061d946e0c9d8bce04a1", size = 39728, upload-time = "2025-12-25T03:53:03.306Z" },
1452
1452
  ]
1453
1453
 
1454
1454
  [[package]]
@@ -2744,15 +2744,15 @@ wheels = [
2744
2744
 
2745
2745
  [[package]]
2746
2746
  name = "sse-starlette"
2747
- version = "3.0.4"
2747
+ version = "3.1.1"
2748
2748
  source = { registry = "https://pypi.org/simple" }
2749
2749
  dependencies = [
2750
2750
  { name = "anyio" },
2751
2751
  { name = "starlette" },
2752
2752
  ]
2753
- sdist = { url = "https://files.pythonhosted.org/packages/17/8b/54651ad49bce99a50fd61a7f19c2b6a79fbb072e693101fbb1194c362054/sse_starlette-3.0.4.tar.gz", hash = "sha256:5e34286862e96ead0eb70f5ddd0bd21ab1f6473a8f44419dd267f431611383dd", size = 22576, upload-time = "2025-12-14T16:22:52.493Z" }
2753
+ sdist = { url = "https://files.pythonhosted.org/packages/62/08/8f554b0e5bad3e4e880521a1686d96c05198471eed860b0eb89b57ea3636/sse_starlette-3.1.1.tar.gz", hash = "sha256:bffa531420c1793ab224f63648c059bcadc412bf9fdb1301ac8de1cf9a67b7fb", size = 24306, upload-time = "2025-12-26T15:22:53.836Z" }
2754
2754
  wheels = [
2755
- { url = "https://files.pythonhosted.org/packages/71/22/8ab1066358601163e1ac732837adba3672f703818f693e179b24e0d3b65c/sse_starlette-3.0.4-py3-none-any.whl", hash = "sha256:32c80ef0d04506ced4b0b6ab8fe300925edc37d26f666afb1874c754895f5dc3", size = 11764, upload-time = "2025-12-14T16:22:51.453Z" },
2755
+ { url = "https://files.pythonhosted.org/packages/e3/31/4c281581a0f8de137b710a07f65518b34bcf333b201cfa06cfda9af05f8a/sse_starlette-3.1.1-py3-none-any.whl", hash = "sha256:bb38f71ae74cfd86b529907a9fda5632195dfa6ae120f214ea4c890c7ee9d436", size = 12442, upload-time = "2025-12-26T15:22:52.911Z" },
2756
2756
  ]
2757
2757
 
2758
2758
  [[package]]
@@ -2944,15 +2944,15 @@ wheels = [
2944
2944
 
2945
2945
  [[package]]
2946
2946
  name = "typer-slim"
2947
- version = "0.20.1"
2947
+ version = "0.21.0"
2948
2948
  source = { registry = "https://pypi.org/simple" }
2949
2949
  dependencies = [
2950
2950
  { name = "click" },
2951
2951
  { name = "typing-extensions" },
2952
2952
  ]
2953
- sdist = { url = "https://files.pythonhosted.org/packages/3f/3d/6a4ec47010e8de34dade20c8e7bce90502b173f62a6b41619523a3fcf562/typer_slim-0.20.1.tar.gz", hash = "sha256:bb9e4f7e6dc31551c8a201383df322b81b0ce37239a5ead302598a2ebb6f7c9c", size = 106113, upload-time = "2025-12-19T16:48:54.206Z" }
2953
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/3b/2f60ce16f578b1db5b8816d37d6a4d9786b33b76407fc8c13b0b86312c31/typer_slim-0.21.0.tar.gz", hash = "sha256:f2dbd150cfa0fead2242e21fa9f654dfc64773763ddf07c6be9a49ad34f79557", size = 106841, upload-time = "2025-12-25T09:54:55.998Z" }
2954
2954
  wheels = [
2955
- { url = "https://files.pythonhosted.org/packages/d8/f9/a273c8b57c69ac1b90509ebda204972265fdc978fbbecc25980786f8c038/typer_slim-0.20.1-py3-none-any.whl", hash = "sha256:8e89c5dbaffe87a4f86f4c7a9e2f7059b5b68c66f558f298969d42ce34f10122", size = 47440, upload-time = "2025-12-19T16:48:52.678Z" },
2955
+ { url = "https://files.pythonhosted.org/packages/b4/84/e97abf10e4a699194ff07fd586ec7f4cf867d9d04bead559a65f9e7aff84/typer_slim-0.21.0-py3-none-any.whl", hash = "sha256:92aee2188ac6fc2b2924bd75bb61a340b78bd8cd51fd9735533ce5a856812c8e", size = 47174, upload-time = "2025-12-25T09:54:54.609Z" },
2956
2956
  ]
2957
2957
 
2958
2958
  [[package]]