Lethal Company v81.5の家具のLuckと販売額の一覧です。

販売額当たりのLuck効率が高い順にソートしています。

QuotaとLuckの関係は、v80から変更されたようです(v72時点のQuotaとLuckの仕様)。 また、v80から復活したVain Shroud・Kidnapper Foxの出現条件にも影響するようになったようです。

抽出方法について、今回はMODではなく、AssetRipperで抽出したUnityアセットを使っています。

v81.5

  • Build ID: 22825947
  • Manifest ID: 6423525044216269478
name luck cost luck/cost*10^5
Disco Ball 0.06 150 40.00
JackOLantern 0.012 50 24.00
Television 0.02 130 15.38
Microwave 0.01 80 12.50
Goldfish 0.006 50 12.00
Dog house 0.007 80 8.75
Electric chair 0.012 140 8.57
Shower 0.015 180 8.33
Welcome mat 0.003 40 7.50
Toilet 0.01 150 6.67
Table 0.004 70 5.71
Sofa chair 0.008 150 5.33
Fridge 0.01 225 4.44
Record player 0.005 120 4.17
Romantic table 0.005 120 4.17
Cozy lights 0.005 140 3.57
Plushie pajama man 0.003 100 3.00
Loud horn 0.0025 100 2.50
Classic painting 0.006 400 1.50
Inverse Teleporter 0.004 425 0.94
Bee Suit 0 110 0.00
Bunny Suit 0 200 0.00
Green suit 0 60 0.00
Hazard suit 0 90 0.00
Pajama suit 0 900 0.00
Purple Suit 0 70 0.00
Teleporter 0 375 0.00
Signal translator -0.012 255 -4.71

v72との比較

  • 新規の家具は追加されていない。
  • Electric chairのLuck値が0.018から0.012に減少。効率は12.86から8.57に減少。

簡易抽出コマンド(家具名とLuck値のみ)

yq コマンド(mikefarah/yq)が利用できる場合、以下のようなコマンドで家具名とLuck値をTSV形式で抽出できます。 販売額との対応付けはできませんが、手早くLuck値を確認したい場合に便利です。

yq -r '
  .MonoBehaviour.unlockables
  | to_entries[]
  | [.key, .value.unlockableName, .value.luckValue]
  | @tsv
' "0_Unlockables.asset"

簡易抽出コマンド(船内アンロック販売物のIDと販売額のみ)

船内アンロック販売物は、以下のようなコマンドで購入対象IDと販売額をTSV形式で抽出できます。 shipUnlockableID0_Unlockables.asset 側の配列 index (.key) と対応し、家具との対応付けに使えます。 buyUnlockable = 1 の確認ノードは除外し、店頭表示に使われる注文ノードだけを対象にしています。

ただし、船内アンロック販売物のデータは、販売物ごとにMonoBehaviourディレクトリ内の複数のアセットに分散しているため、全ての販売物を抽出するには、MonoBehaviourディレクトリ内の全アセットを対象に同様のコマンドを走らせて集約する必要があります。

yq -r '
  select(.MonoBehaviour.shipUnlockableID >= 0)
  | select(.MonoBehaviour.itemCost > 0)
  | select(.MonoBehaviour.buyUnlockable == 0)
  | [.MonoBehaviour.shipUnlockableID, .MonoBehaviour.itemCost]
  | @tsv
' "DiscoBallBuy.asset"

アセットファイル名の例

  • DiscoBallBuy.asset
  • TelevisionBuy1.asset
  • SignalTranslatorBuy.asset

抽出スクリプト

uv scriptとして実行することを想定した Python スクリプト dump_furniture_luck_tsv.py です。

  • uv 0.11
  • Python 3.14

dump_furniture_luck_tsv.py は、Unityアセットから家具のLuckと販売額を抽出し、TSV形式で標準出力に書き出します。

uv run dump_furniture_luck_tsv.py --asset_file path/to/AssetRipperOutputDir/ > output.tsv

uv run dump_furniture_luck_tsv.py --asset_file path/to/0_Unlockables.asset > output.tsv

dump_furniture_luck_tsv.py

Details
# /// script
# requires-python = ">=3.14"
# dependencies = [
#     "aiofiles>=24,<25",
#     "pydantic>=2.11,<3",
#     "pydantic-settings>=2,<3",
#     "pyyaml>=6.0.2,<7",
# ]
#
# [tool.uv]
# exclude-newer = "P7D"
# ///
"""Lethal Company の抽出済み Unity アセットから家具 luck の TSV を出力する。

設計方針:

- 依存関係は uv script metadata と lockfile で管理する。
- サプライチェーン攻撃対策として、依存解決時は必ず7日間のクールダウンを適用する。
  uv では `exclude-newer = "P7D"` を使う。
- Unity YAML の読み込みは PyYAML に任せる。
- Unity 独自タグの吸収だけを `UnityYamlLoader` に閉じ込める。
- CLI 引数は pydantic-settings の標準的な CLI 解析に寄せる。
- アセット探索は既知の入力形に絞り、巨大な抽出アセット全体の再帰探索を避ける。
- ファイル I/O は aiofiles に寄せ、YAML parse と Pydantic validation は `asyncio.to_thread`
  に逃がし、イベントループを長く塞がない。
- Unity アセットのフィールド構造と 1:1 に対応する検証は Pydantic model に寄せる。
- 集計・表示のために整形した値は dataclass に詰め替え、アセット構造と分ける。
- 自前の関数と dataclass は宣言レベルでキーワード引数を強制し、値の取り違えを防ぐ。
- mypy strict 相当の型チェックを通過するように、曖昧な型や暗黙の詰め替えを避ける。
- 型安全でない cast は使わず、条件分岐で型を絞り込む。
- 行幅は 88 文字以内を目安にし、ruff で潰れない範囲で論理ブロックに空行を置く。
- docstring は関数の役割とインターフェース、行コメントは局所的な設計意図を書く。

作成補助:

- OpenAI Codex, GPT-5.5
"""

from __future__ import annotations

import asyncio
from dataclasses import dataclass
from itertools import batched
from pathlib import Path
from typing import Any, TypeVar

import aiofiles
import aiofiles.os
import aiofiles.ospath
from pydantic import BaseModel, ConfigDict, Field
from pydantic_settings import BaseSettings, CliApp
import yaml
from yaml.nodes import MappingNode, Node, ScalarNode, SequenceNode


ModelT = TypeVar("ModelT", bound=BaseModel)

# asset の読み込み、YAML parse、Pydantic validation を同時に走らせる件数の上限。
MAX_CONCURRENT_ASSET_LOADS = 32


# Raw model は Unity アセット上のキー名をそのまま持つ。
# Python 側の読みやすい名前へ変えるのは、dataclass へ詰め替える段階で行う。
class UnlockableRaw(BaseModel):
    model_config = ConfigDict(extra="ignore", strict=True)

    unlockableName: str
    luckValue: int | float


class UnlockablesAssetRaw(BaseModel):
    model_config = ConfigDict(extra="ignore", strict=True)

    unlockables: list[UnlockableRaw]


class TerminalNodeRaw(BaseModel):
    model_config = ConfigDict(extra="ignore", strict=True)

    # TerminalNode では、家具購入ノード以外にも同じ asset 形式が大量に存在する。
    # 欠けたフィールドを既定値で受け、後段の条件で家具購入ノードだけを残す。
    shipUnlockableID: int = -1
    itemCost: int = 0
    buyUnlockable: int = 0


# ここから下の dataclass は、TSV 出力に必要な情報だけを持つ。
# 生アセットのキー名や余分な Unity フィールドを、集計処理へ持ち込まないための境界。
@dataclass(frozen=True, kw_only=True)
class Unlockable:
    id: int
    name: str
    luck: float


@dataclass(frozen=True, kw_only=True)
class TerminalNodePurchase:
    unlockable_id: int
    cost: int


@dataclass(frozen=True, kw_only=True)
class Row:
    """TSV へ出力する 1 行分の家具 luck 情報。"""

    name: str
    luck: float
    cost: int

    @property
    def efficiency(self) -> float:
        """価格 100,000 あたりの luck を返す。"""

        return self.luck / self.cost * 100_000


class CliSettings(BaseSettings):
    """pydantic-settings の CLI として受け取るスクリプト引数。"""

    asset_file: Path = Field(
        description=(
            "Path to 0_Unlockables.asset, MonoBehaviour, "
            "ExportedProject/Assets, ExportedProject, or the extracted asset root."
        ),
    )

    async def cli_cmd(self) -> None:
        """家具 luck の TSV を標準出力へ書き出す。"""

        unlockables_asset = await find_unlockables_asset(path=self.asset_file)
        print("name\tluck\tcost\tluck/cost*10^5")

        for row in await build_rows(unlockables_asset=unlockables_asset):
            print(f"{row.name}\t{row.luck:g}\t{row.cost}\t{row.efficiency:.2f}")


class UnityYamlLoader(yaml.SafeLoader):
    """Unity アセット YAML を、このスクリプト用に読む loader。

    SafeLoader へ直接 Unity タグの扱いを登録すると、同じ Python プロセス内の
    他の SafeLoader 利用にも影響する。専用 loader に閉じ込めて、このスクリプトが
    Unity アセットを読む時だけ Unity タグを許容する。
    """

    pass


def construct_unity_object(
    loader: UnityYamlLoader,
    _tag_suffix: str,
    node: Node,
) -> Any:
    """Unity 固有タグ付き node を、通常の YAML 値としてデシリアライズする。

    このスクリプトの自前関数はキーワード専用引数を基本にしている。
    ただし、この関数は PyYAML の multi-constructor から位置引数で呼ばれる
    コールバックなので、PyYAML 側のインターフェースに合わせる。

    YAML のタグは node の種類やアプリケーション固有の意味づけを表す。
    Unity の YAML は `--- !u!114` のようなタグで Unity のクラス ID を表し、
    例えば 114 は MonoBehaviour に対応する。

    SafeLoader は標準的な YAML タグは扱えるが、Unity 固有タグの
    デシリアライズ規則は持っていない。そのままでは Unity タグを見た時点で
    読み込みに失敗するため、専用 loader でタグを無視し、SafeLoader 同等の処理で読む。
    """

    if isinstance(node, MappingNode):
        return loader.construct_mapping(node, deep=True)
    if isinstance(node, SequenceNode):
        return loader.construct_sequence(node, deep=True)
    if isinstance(node, ScalarNode):
        return loader.construct_scalar(node)
    raise TypeError(f"unsupported Unity YAML node: {type(node).__name__}")


UnityYamlLoader.add_multi_constructor("tag:unity3d.com,2011:", construct_unity_object)


def validate_str_key_dict(*, value: object) -> dict[str, Any]:
    """値が str キーの dict であることを検証し、型付き dict として返す。"""

    if not isinstance(value, dict):
        raise ValueError("value must be a dictionary")

    result: dict[str, Any] = {}
    for key, item in value.items():
        if not isinstance(key, str):
            raise ValueError(f"value contains a non-string key: {key!r}")
        result[key] = item

    return result


def parse_monobehaviour(*, text: str) -> dict[str, Any]:
    """Unity YAML 文字列から `MonoBehaviour` document を取り出す。"""

    # PyYAML が返す値は型チェッカーから見ると曖昧なので、ここでは str キーの
    # dict であることだけ確認し、フィールド構造の検証は直後の Pydantic model に任せる。
    for document in yaml.load_all(text, Loader=UnityYamlLoader):
        document_mapping = validate_str_key_dict(
            value=document,
        )

        # Unity asset は複数 document を持つことがあるため、MonoBehaviour でない
        # document はスキップする。
        mono_behaviour = document_mapping.get("MonoBehaviour")
        if mono_behaviour is None:
            continue

        return validate_str_key_dict(
            value=mono_behaviour,
        )

    raise ValueError("could not find MonoBehaviour document")


async def find_unlockables_asset(*, path: Path) -> Path:
    """入力パスから `0_Unlockables.asset` の実ファイルパスを求める。"""

    # 抽出アセットは巨大なので、親ディレクトリ全体を再帰探索しない。
    # 入力は 0_Unlockables.asset 直指定、MonoBehaviour、Assets、ExportedProject、
    # または抽出アセットルートの想定に絞る。
    if await aiofiles.ospath.isfile(path):
        return path

    candidates = [
        path / "0_Unlockables.asset",
        path / "MonoBehaviour" / "0_Unlockables.asset",
        path / "Assets" / "MonoBehaviour" / "0_Unlockables.asset",
        path / "ExportedProject" / "Assets" / "MonoBehaviour" / "0_Unlockables.asset",
    ]

    for candidate in candidates:
        if await aiofiles.ospath.isfile(candidate):
            return candidate

    raise FileNotFoundError(f"could not find 0_Unlockables.asset from {path}")


async def read_asset_text(*, path: Path) -> str:
    """Unity asset ファイルの本文を UTF-8 で読み込む。"""

    async with aiofiles.open(path, encoding="utf-8") as file:
        return await file.read()


async def load_monobehaviour_model(*, path: Path, model: type[ModelT]) -> ModelT:
    """Unity asset の `MonoBehaviour` document を、指定された Pydantic model として読み込む。"""

    text = await read_asset_text(path=path)

    def parse_and_validate() -> ModelT:
        try:
            mono_behaviour = parse_monobehaviour(text=text)
        except ValueError as exc:
            message = f"could not find MonoBehaviour document in {path}"
            raise ValueError(message) from exc
        return model.model_validate(mono_behaviour)

    # YAML parse と Pydantic validation は同期的な CPU バウンド処理である。
    # async な呼び出し経路でイベントループを長く塞がないように、別スレッドへ逃がす。
    return await asyncio.to_thread(parse_and_validate)


async def parse_unlockables(*, path: Path) -> list[Unlockable]:
    """`0_Unlockables.asset` を TSV 用の unlockable 一覧へ変換する。"""

    # `0_Unlockables.asset` は unlockables 配列の順番そのものが
    # TerminalNode 側の `shipUnlockableID` と対応するため、enumerate の index を ID として保持する。
    raw = await load_monobehaviour_model(path=path, model=UnlockablesAssetRaw)
    return [
        Unlockable(
            id=unlockable_id,
            name=unlockable.unlockableName,
            luck=float(unlockable.luckValue),
        )
        for unlockable_id, unlockable in enumerate(raw.unlockables)
    ]


async def parse_terminal_node_purchase(*, path: Path) -> TerminalNodePurchase | None:
    """購入用 TerminalNode なら購入対象と価格を返す。"""

    raw = await load_monobehaviour_model(path=path, model=TerminalNodeRaw)
    unlockable_id = raw.shipUnlockableID
    cost = raw.itemCost
    buy_unlockable = raw.buyUnlockable

    # 同じ unlockable には注文ノードと確認ノードがある。
    # 販売額として使うのは、店頭で確認前に表示される注文ノードの `itemCost`。
    # 確認ノードは `buyUnlockable == 1` なので除外する。
    if unlockable_id >= 0 and cost > 0 and buy_unlockable == 0:
        return TerminalNodePurchase(unlockable_id=unlockable_id, cost=cost)

    return None


async def load_terminal_node_purchases(*, root: Path) -> list[TerminalNodePurchase]:
    """MonoBehaviour ディレクトリ内の購入用 TerminalNode から購入情報を読み込む。"""

    names = await aiofiles.os.listdir(root)
    # MonoBehaviour ディレクトリには多数の asset があり、TerminalNode かどうかは
    # 読み込むまで確定できない。そのため、候補 asset を順に読み込んで判定する。
    paths = [
        root / name
        for name in names
        if name.endswith(".asset") and name != "0_Unlockables.asset"
    ]

    results: list[TerminalNodePurchase | None] = []
    for batch in batched(paths, MAX_CONCURRENT_ASSET_LOADS):
        results.extend(
            await asyncio.gather(
                *(parse_terminal_node_purchase(path=path) for path in batch)
            )
        )

    return [result for result in results if result is not None]


async def build_rows(*, unlockables_asset: Path) -> list[Row]:
    """unlockable と購入価格を突き合わせ、効率順の TSV 行を作る。"""

    unlockables, terminal_nodes = await asyncio.gather(
        parse_unlockables(path=unlockables_asset),
        load_terminal_node_purchases(root=unlockables_asset.parent),
    )

    costs = {
        terminal_node.unlockable_id: terminal_node.cost
        for terminal_node in terminal_nodes
    }
    rows = [
        Row(name=item.name, luck=item.luck, cost=costs[item.id])
        for item in unlockables
        if item.id in costs
    ]

    return sorted(rows, key=lambda row: (-row.efficiency, row.name.lower()))


def main() -> None:
    """CLI エントリーポイント。"""

    CliApp.run(CliSettings)


if __name__ == "__main__":
    main()

dump_furniture_luck_tsv.py.lock

Details
version = 1
revision = 3
requires-python = ">=3.14"

[options]
exclude-newer = "2026-05-23T01:18:38.2945056Z"
exclude-newer-span = "P7D"

[manifest]
requirements = [
    { name = "aiofiles", specifier = ">=24,<25" },
    { name = "pydantic", specifier = ">=2.11,<3" },
    { name = "pydantic-settings", specifier = ">=2,<3" },
    { name = "pyyaml", specifier = ">=6.0.2,<7" },
]

[[package]]
name = "aiofiles"
version = "24.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" },
]

[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
]

[[package]]
name = "pydantic"
version = "2.13.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "annotated-types" },
    { name = "pydantic-core" },
    { name = "typing-extensions" },
    { name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
]

[[package]]
name = "pydantic-core"
version = "2.46.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
    { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
    { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
    { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
    { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
    { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
    { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
    { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
    { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
    { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
    { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
    { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
    { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
    { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
    { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
    { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
    { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
    { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
    { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
    { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
    { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
    { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
    { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
    { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
    { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
    { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
    { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
    { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
    { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
    { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
]

[[package]]
name = "pydantic-settings"
version = "2.14.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "pydantic" },
    { name = "python-dotenv" },
    { name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" },
]

[[package]]
name = "python-dotenv"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
]

[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
    { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
    { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
    { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
    { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
    { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
    { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
    { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
    { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
    { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
    { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
    { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
    { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
    { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
    { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
    { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
    { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
    { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]

[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]

[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
]