diff --git a/README.md b/README.md index 88656160..16aac996 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,13 @@ Allows you to play roleplay scenarios with large language models. - -|![Screenshot 1](docs/img/0.17.0/ss-1.png)|![Screenshot 2](docs/img/0.17.0/ss-2.png)| +||| |------------------------------------------|------------------------------------------| -|![Screenshot 1](docs/img/0.17.0/ss-4.png)|![Screenshot 2](docs/img/0.17.0/ss-3.png)| +|![Screenshot 1](docs/img/0.19.0/Screenshot_15.png)|![Screenshot 2](docs/img/0.19.0/Screenshot_16.png)| +|![Screenshot 3](docs/img/0.19.0/Screenshot_17.png)|![Screenshot 3](docs/img/0.17.0/ss-1.png)| +|![Screenshot 3](docs/img/0.17.0/ss-2.png)|![Screenshot 4](docs/img/0.17.0/ss-4.png)| +||| + > :warning: **It does not run any large language models itself but relies on existing APIs. Currently supports OpenAI, text-generation-webui and LMStudio. 0.18.0 also adds support for generic OpenAI api implementations, but generation quality on that will vary.** diff --git a/config.example.yaml b/config.example.yaml index c54a3fea..a744eecd 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -48,6 +48,7 @@ game: # embeddings: instructor # instructor_device: cuda # instructor_model: hkunlp/instructor-xl +# openai_model: text-embedding-3-small ## Remote LLMs diff --git a/docs/chromadb.md b/docs/chromadb.md index e8906cf5..b7d7a4a8 100644 --- a/docs/chromadb.md +++ b/docs/chromadb.md @@ -56,6 +56,7 @@ Then add the following to `config.yaml` for chromadb: ```yaml chromadb: embeddings: openai + openai_model: text-embedding-3-small ``` **Note**: As with everything openai, using this isn't free. It's way cheaper than their text completion though. ALSO - if you send super explicit content they may flag / ban your key, so keep that in mind (i hear they usually send warnings first though), and always monitor your usage on their dashboard. \ No newline at end of file diff --git a/docs/dev/agents/example/test/__init__.py b/docs/dev/agents/example/test/__init__.py new file mode 100644 index 00000000..f0ef7e68 --- /dev/null +++ b/docs/dev/agents/example/test/__init__.py @@ -0,0 +1,48 @@ +from talemate.agents.base import Agent, AgentAction +from talemate.agents.registry import register +from talemate.events import GameLoopEvent +import talemate.emit.async_signals +from talemate.emit import emit + +@register() +class TestAgent(Agent): + + agent_type = "test" + verbose_name = "Test" + + def __init__(self, client): + self.client = client + self.is_enabled = True + self.actions = { + "test": AgentAction( + enabled=True, + label="Test", + description="Test", + ), + } + + @property + def enabled(self): + return self.is_enabled + + @property + def has_toggle(self): + return True + + @property + def experimental(self): + return True + + def connect(self, scene): + super().connect(scene) + talemate.emit.async_signals.get("game_loop").connect(self.on_game_loop) + + async def on_game_loop(self, emission: GameLoopEvent): + """ + Called on the beginning of every game loop + """ + + if not self.enabled: + return + + emit("status", status="info", message="Annoying you with a test message every game loop.") \ No newline at end of file diff --git a/docs/dev/client/example/test/__init__.py b/docs/dev/client/example/test/__init__.py new file mode 100644 index 00000000..449e9eec --- /dev/null +++ b/docs/dev/client/example/test/__init__.py @@ -0,0 +1,67 @@ +import pydantic +from openai import AsyncOpenAI + +from talemate.client.base import ClientBase +from talemate.client.registry import register + + +class Defaults(pydantic.BaseModel): + api_url: str = "http://localhost:1234" + max_token_length: int = 4096 + +@register() +class TestClient(ClientBase): + client_type = "test" + + class Meta(ClientBase.Meta): + name_prefix: str = "test" + title: str = "Test" + defaults: Defaults = Defaults() + + def set_client(self, **kwargs): + self.client = AsyncOpenAI(base_url=self.api_url + "/v1", api_key="sk-1111") + + def tune_prompt_parameters(self, parameters: dict, kind: str): + + """ + Talemate adds a bunch of parameters to the prompt, but not all of them are valid for all clients. + + This method is called before the prompt is sent to the client, and it allows the client to remove + any parameters that it doesn't support. + """ + + super().tune_prompt_parameters(parameters, kind) + + keys = list(parameters.keys()) + + valid_keys = ["temperature", "top_p"] + + for key in keys: + if key not in valid_keys: + del parameters[key] + + async def get_model_name(self): + + """ + This should return the name of the model that is being used. + """ + + return "Mock test model" + + async def generate(self, prompt: str, parameters: dict, kind: str): + """ + Generates text from the given prompt and parameters. + """ + human_message = {"role": "user", "content": prompt.strip()} + + self.log.debug("generate", prompt=prompt[:128] + " ...", parameters=parameters) + + try: + response = await self.client.chat.completions.create( + model=self.model_name, messages=[human_message], **parameters + ) + + return response.choices[0].message.content + except Exception as e: + self.log.error("generate error", e=e) + return "" diff --git a/docs/img/0.19.0/Screenshot_15.png b/docs/img/0.19.0/Screenshot_15.png new file mode 100644 index 00000000..8b2bde4b Binary files /dev/null and b/docs/img/0.19.0/Screenshot_15.png differ diff --git a/docs/img/0.19.0/Screenshot_16.png b/docs/img/0.19.0/Screenshot_16.png new file mode 100644 index 00000000..cc8ff04a Binary files /dev/null and b/docs/img/0.19.0/Screenshot_16.png differ diff --git a/docs/img/0.19.0/Screenshot_17.png b/docs/img/0.19.0/Screenshot_17.png new file mode 100644 index 00000000..fd9659aa Binary files /dev/null and b/docs/img/0.19.0/Screenshot_17.png differ diff --git a/docs/tts.md b/docs/tts.md index f7bdc510..d40f7f2f 100644 --- a/docs/tts.md +++ b/docs/tts.md @@ -17,21 +17,6 @@ elevenlabs: api_key: ``` -## Configuring Coqui TTS - -To use Coqui TTS with Talemate, follow these steps: - -1. Visit [Coqui](https://app.coqui.ai) and sign up for an account. -2. Go to the [account page](https://app.coqui.ai/account) and scroll to the bottom to find your API key. -3. In the `config.yaml` file, under the `coqui` section, set the `api_key` field with your Coqui API key. - -Example configuration snippet: - -```yaml -coqui: - api_key: -``` - ## Configuring Local TTS API For running a local TTS API, Talemate requires specific dependencies to be installed. diff --git a/poetry.lock b/poetry.lock index b1716780..80787bba 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,87 +16,87 @@ pycares = ">=4.0.0" [[package]] name = "aiohttp" -version = "3.9.1" +version = "3.9.2" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, - {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, - {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, - {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, - {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, - {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, - {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, - {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, - {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, - {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, - {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, - {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, - {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, - {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, + {file = "aiohttp-3.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:772fbe371788e61c58d6d3d904268e48a594ba866804d08c995ad71b144f94cb"}, + {file = "aiohttp-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:edd4f1af2253f227ae311ab3d403d0c506c9b4410c7fc8d9573dec6d9740369f"}, + {file = "aiohttp-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cfee9287778399fdef6f8a11c9e425e1cb13cc9920fd3a3df8f122500978292b"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc158466f6a980a6095ee55174d1de5730ad7dec251be655d9a6a9dd7ea1ff9"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54ec82f45d57c9a65a1ead3953b51c704f9587440e6682f689da97f3e8defa35"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abeb813a18eb387f0d835ef51f88568540ad0325807a77a6e501fed4610f864e"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc91d07280d7d169f3a0f9179d8babd0ee05c79d4d891447629ff0d7d8089ec2"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b65e861f4bebfb660f7f0f40fa3eb9f2ab9af10647d05dac824390e7af8f75b7"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04fd8ffd2be73d42bcf55fd78cde7958eeee6d4d8f73c3846b7cba491ecdb570"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3d8d962b439a859b3ded9a1e111a4615357b01620a546bc601f25b0211f2da81"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:8ceb658afd12b27552597cf9a65d9807d58aef45adbb58616cdd5ad4c258c39e"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0e4ee4df741670560b1bc393672035418bf9063718fee05e1796bf867e995fad"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2dec87a556f300d3211decf018bfd263424f0690fcca00de94a837949fbcea02"}, + {file = "aiohttp-3.9.2-cp310-cp310-win32.whl", hash = "sha256:3e1a800f988ce7c4917f34096f81585a73dbf65b5c39618b37926b1238cf9bc4"}, + {file = "aiohttp-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:ea510718a41b95c236c992b89fdfc3d04cc7ca60281f93aaada497c2b4e05c46"}, + {file = "aiohttp-3.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6aaa6f99256dd1b5756a50891a20f0d252bd7bdb0854c5d440edab4495c9f973"}, + {file = "aiohttp-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a27d8c70ad87bcfce2e97488652075a9bdd5b70093f50b10ae051dfe5e6baf37"}, + {file = "aiohttp-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54287bcb74d21715ac8382e9de146d9442b5f133d9babb7e5d9e453faadd005e"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb3d05569aa83011fcb346b5266e00b04180105fcacc63743fc2e4a1862a891"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8534e7d69bb8e8d134fe2be9890d1b863518582f30c9874ed7ed12e48abe3c4"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bd9d5b989d57b41e4ff56ab250c5ddf259f32db17159cce630fd543376bd96b"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa6904088e6642609981f919ba775838ebf7df7fe64998b1a954fb411ffb4663"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda42eb410be91b349fb4ee3a23a30ee301c391e503996a638d05659d76ea4c2"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:193cc1ccd69d819562cc7f345c815a6fc51d223b2ef22f23c1a0f67a88de9a72"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b9f1cb839b621f84a5b006848e336cf1496688059d2408e617af33e3470ba204"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:d22a0931848b8c7a023c695fa2057c6aaac19085f257d48baa24455e67df97ec"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4112d8ba61fbd0abd5d43a9cb312214565b446d926e282a6d7da3f5a5aa71d36"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c4ad4241b52bb2eb7a4d2bde060d31c2b255b8c6597dd8deac2f039168d14fd7"}, + {file = "aiohttp-3.9.2-cp311-cp311-win32.whl", hash = "sha256:ee2661a3f5b529f4fc8a8ffee9f736ae054adfb353a0d2f78218be90617194b3"}, + {file = "aiohttp-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:4deae2c165a5db1ed97df2868ef31ca3cc999988812e82386d22937d9d6fed52"}, + {file = "aiohttp-3.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6f4cdba12539215aaecf3c310ce9d067b0081a0795dd8a8805fdb67a65c0572a"}, + {file = "aiohttp-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:84e843b33d5460a5c501c05539809ff3aee07436296ff9fbc4d327e32aa3a326"}, + {file = "aiohttp-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8008d0f451d66140a5aa1c17e3eedc9d56e14207568cd42072c9d6b92bf19b52"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61c47ab8ef629793c086378b1df93d18438612d3ed60dca76c3422f4fbafa792"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc71f748e12284312f140eaa6599a520389273174b42c345d13c7e07792f4f57"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1c3a4d0ab2f75f22ec80bca62385db2e8810ee12efa8c9e92efea45c1849133"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a87aa0b13bbee025faa59fa58861303c2b064b9855d4c0e45ec70182bbeba1b"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2cc0d04688b9f4a7854c56c18aa7af9e5b0a87a28f934e2e596ba7e14783192"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1956e3ac376b1711c1533266dec4efd485f821d84c13ce1217d53e42c9e65f08"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:114da29f39eccd71b93a0fcacff178749a5c3559009b4a4498c2c173a6d74dff"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3f17999ae3927d8a9a823a1283b201344a0627272f92d4f3e3a4efe276972fe8"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:f31df6a32217a34ae2f813b152a6f348154f948c83213b690e59d9e84020925c"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7a75307ffe31329928a8d47eae0692192327c599113d41b278d4c12b54e1bd11"}, + {file = "aiohttp-3.9.2-cp312-cp312-win32.whl", hash = "sha256:972b63d589ff8f305463593050a31b5ce91638918da38139b9d8deaba9e0fed7"}, + {file = "aiohttp-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:200dc0246f0cb5405c80d18ac905c8350179c063ea1587580e3335bfc243ba6a"}, + {file = "aiohttp-3.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:158564d0d1020e0d3fe919a81d97aadad35171e13e7b425b244ad4337fc6793a"}, + {file = "aiohttp-3.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:da1346cd0ccb395f0ed16b113ebb626fa43b7b07fd7344fce33e7a4f04a8897a"}, + {file = "aiohttp-3.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:eaa9256de26ea0334ffa25f1913ae15a51e35c529a1ed9af8e6286dd44312554"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1543e7fb00214fb4ccead42e6a7d86f3bb7c34751ec7c605cca7388e525fd0b4"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:186e94570433a004e05f31f632726ae0f2c9dee4762a9ce915769ce9c0a23d89"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d52d20832ac1560f4510d68e7ba8befbc801a2b77df12bd0cd2bcf3b049e52a4"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c45e4e815ac6af3b72ca2bde9b608d2571737bb1e2d42299fc1ffdf60f6f9a1"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa906b9bdfd4a7972dd0628dbbd6413d2062df5b431194486a78f0d2ae87bd55"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:68bbee9e17d66f17bb0010aa15a22c6eb28583edcc8b3212e2b8e3f77f3ebe2a"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4c189b64bd6d9a403a1a3f86a3ab3acbc3dc41a68f73a268a4f683f89a4dec1f"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8a7876f794523123bca6d44bfecd89c9fec9ec897a25f3dd202ee7fc5c6525b7"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d23fba734e3dd7b1d679b9473129cd52e4ec0e65a4512b488981a56420e708db"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b141753be581fab842a25cb319f79536d19c2a51995d7d8b29ee290169868eab"}, + {file = "aiohttp-3.9.2-cp38-cp38-win32.whl", hash = "sha256:103daf41ff3b53ba6fa09ad410793e2e76c9d0269151812e5aba4b9dd674a7e8"}, + {file = "aiohttp-3.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:328918a6c2835861ff7afa8c6d2c70c35fdaf996205d5932351bdd952f33fa2f"}, + {file = "aiohttp-3.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5264d7327c9464786f74e4ec9342afbbb6ee70dfbb2ec9e3dfce7a54c8043aa3"}, + {file = "aiohttp-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07205ae0015e05c78b3288c1517afa000823a678a41594b3fdc870878d645305"}, + {file = "aiohttp-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0a1e638cffc3ec4d4784b8b4fd1cf28968febc4bd2718ffa25b99b96a741bd"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d43302a30ba1166325974858e6ef31727a23bdd12db40e725bec0f759abce505"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16a967685907003765855999af11a79b24e70b34dc710f77a38d21cd9fc4f5fe"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fa3ee92cd441d5c2d07ca88d7a9cef50f7ec975f0117cd0c62018022a184308"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b500c5ad9c07639d48615a770f49618130e61be36608fc9bc2d9bae31732b8f"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c07327b368745b1ce2393ae9e1aafed7073d9199e1dcba14e035cc646c7941bf"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cc7d6502c23a0ec109687bf31909b3fb7b196faf198f8cff68c81b49eb316ea9"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:07be2be7071723c3509ab5c08108d3a74f2181d4964e869f2504aaab68f8d3e8"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:122468f6fee5fcbe67cb07014a08c195b3d4c41ff71e7b5160a7bcc41d585a5f"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:00a9abcea793c81e7f8778ca195a1714a64f6d7436c4c0bb168ad2a212627000"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a9825fdd64ecac5c670234d80bb52bdcaa4139d1f839165f548208b3779c6c6"}, + {file = "aiohttp-3.9.2-cp39-cp39-win32.whl", hash = "sha256:5422cd9a4a00f24c7244e1b15aa9b87935c85fb6a00c8ac9b2527b38627a9211"}, + {file = "aiohttp-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:7d579dcd5d82a86a46f725458418458fa43686f6a7b252f2966d359033ffc8ab"}, + {file = "aiohttp-3.9.2.tar.gz", hash = "sha256:b0ad0a5e86ce73f5368a164c10ada10504bf91869c05ab75d982c6048217fbf7"}, ] [package.dependencies] @@ -324,33 +324,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "23.12.1" +version = "24.1.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, + {file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"}, + {file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"}, + {file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"}, + {file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"}, + {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"}, + {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"}, + {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"}, + {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"}, + {file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"}, + {file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"}, + {file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"}, + {file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"}, + {file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"}, + {file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"}, + {file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"}, + {file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"}, + {file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"}, + {file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"}, + {file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"}, + {file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"}, + {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"}, + {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"}, ] [package.dependencies] @@ -381,17 +381,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.27" +version = "1.34.31" description = "The AWS SDK for Python" optional = false python-versions = ">= 3.8" files = [ - {file = "boto3-1.34.27-py3-none-any.whl", hash = "sha256:3626db4ba9fbb1b58c8fe923da5ed670873b3d881a102956ea19d3b69cd097cc"}, - {file = "boto3-1.34.27.tar.gz", hash = "sha256:ebdd938019f3df2e7b50585353963d4553faf3fbb7b2085c440107fa6caa233b"}, + {file = "boto3-1.34.31-py3-none-any.whl", hash = "sha256:0d800130e43a5d4e71300cc6f91aabcef6fe6f26bc206bc61374bf695049587a"}, + {file = "boto3-1.34.31.tar.gz", hash = "sha256:c4dec7ea9bc9210ec783d39b56d332f5a266b0d1e31a96c5092f6bd5252361ba"}, ] [package.dependencies] -botocore = ">=1.34.27,<1.35.0" +botocore = ">=1.34.31,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -400,13 +400,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.27" +version = "1.34.31" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">= 3.8" files = [ - {file = "botocore-1.34.27-py3-none-any.whl", hash = "sha256:1c10f247136ad17b6ef1588c1e043e294dbaebdebe9ce84dc56713029f515c53"}, - {file = "botocore-1.34.27.tar.gz", hash = "sha256:a0e68ba264275b358b8c1cca604161f4d9465cf7847d73e929543a9f30ff22d1"}, + {file = "botocore-1.34.31-py3-none-any.whl", hash = "sha256:6ee1ba451ce3d640dccd485906f68a55d9e7f3534553876e4adc75d6060a05ac"}, + {file = "botocore-1.34.31.tar.gz", hash = "sha256:d5a2153dbe9687f510f179e03913bc9b4e266c865cabebe440c4d05ab923faa7"}, ] [package.dependencies] @@ -877,43 +877,43 @@ cron = ["capturer (>=2.4)"] [[package]] name = "cryptography" -version = "42.0.0" +version = "42.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:c640b0ef54138fde761ec99a6c7dc4ce05e80420262c20fa239e694ca371d434"}, - {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:678cfa0d1e72ef41d48993a7be75a76b0725d29b820ff3cfd606a5b2b33fda01"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:146e971e92a6dd042214b537a726c9750496128453146ab0ee8971a0299dc9bd"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87086eae86a700307b544625e3ba11cc600c3c0ef8ab97b0fda0705d6db3d4e3"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a68bfcf57a6887818307600c3c0ebc3f62fbb6ccad2240aa21887cda1f8df1b"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5a217bca51f3b91971400890905a9323ad805838ca3fa1e202a01844f485ee87"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ca20550bb590db16223eb9ccc5852335b48b8f597e2f6f0878bbfd9e7314eb17"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:33588310b5c886dfb87dba5f013b8d27df7ffd31dc753775342a1e5ab139e59d"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9515ea7f596c8092fdc9902627e51b23a75daa2c7815ed5aa8cf4f07469212ec"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:35cf6ed4c38f054478a9df14f03c1169bb14bd98f0b1705751079b25e1cb58bc"}, - {file = "cryptography-42.0.0-cp37-abi3-win32.whl", hash = "sha256:8814722cffcfd1fbd91edd9f3451b88a8f26a5fd41b28c1c9193949d1c689dc4"}, - {file = "cryptography-42.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:a2a8d873667e4fd2f34aedab02ba500b824692c6542e017075a2efc38f60a4c0"}, - {file = "cryptography-42.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8fedec73d590fd30c4e3f0d0f4bc961aeca8390c72f3eaa1a0874d180e868ddf"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be41b0c7366e5549265adf2145135dca107718fa44b6e418dc7499cfff6b4689"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ca482ea80626048975360c8e62be3ceb0f11803180b73163acd24bf014133a0"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c58115384bdcfe9c7f644c72f10f6f42bed7cf59f7b52fe1bf7ae0a622b3a139"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:56ce0c106d5c3fec1038c3cca3d55ac320a5be1b44bf15116732d0bc716979a2"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:324721d93b998cb7367f1e6897370644751e5580ff9b370c0a50dc60a2003513"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d97aae66b7de41cdf5b12087b5509e4e9805ed6f562406dfcf60e8481a9a28f8"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:85f759ed59ffd1d0baad296e72780aa62ff8a71f94dc1ab340386a1207d0ea81"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:206aaf42e031b93f86ad60f9f5d9da1b09164f25488238ac1dc488334eb5e221"}, - {file = "cryptography-42.0.0-cp39-abi3-win32.whl", hash = "sha256:74f18a4c8ca04134d2052a140322002fef535c99cdbc2a6afc18a8024d5c9d5b"}, - {file = "cryptography-42.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:14e4b909373bc5bf1095311fa0f7fcabf2d1a160ca13f1e9e467be1ac4cbdf94"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3005166a39b70c8b94455fdbe78d87a444da31ff70de3331cdec2c568cf25b7e"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:be14b31eb3a293fc6e6aa2807c8a3224c71426f7c4e3639ccf1a2f3ffd6df8c3"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:bd7cf7a8d9f34cc67220f1195884151426ce616fdc8285df9054bfa10135925f"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c310767268d88803b653fffe6d6f2f17bb9d49ffceb8d70aed50ad45ea49ab08"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bdce70e562c69bb089523e75ef1d9625b7417c6297a76ac27b1b8b1eb51b7d0f"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e9326ca78111e4c645f7e49cbce4ed2f3f85e17b61a563328c85a5208cf34440"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:69fd009a325cad6fbfd5b04c711a4da563c6c4854fc4c9544bff3088387c77c0"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:988b738f56c665366b1e4bfd9045c3efae89ee366ca3839cd5af53eaa1401bce"}, - {file = "cryptography-42.0.0.tar.gz", hash = "sha256:6cf9b76d6e93c62114bd19485e5cb003115c134cf9ce91f8ac924c44f8c8c3f4"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, + {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, + {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, + {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, + {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, + {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, + {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, ] [package.dependencies] @@ -2227,12 +2227,12 @@ nvidia-nvjitlink-cu12 = "*" [[package]] name = "nvidia-nccl-cu12" -version = "2.18.1" +version = "2.19.3" description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" files = [ - {file = "nvidia_nccl_cu12-2.18.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:1a6c4acefcbebfa6de320f412bf7866de856e786e0462326ba1bac40de0b5e71"}, + {file = "nvidia_nccl_cu12-2.19.3-py3-none-manylinux1_x86_64.whl", hash = "sha256:a9734707a2c96443331c1e48c717024aa6678a0e2a4cb66b2c364d18cee6b48d"}, ] [[package]] @@ -2316,13 +2316,13 @@ sympy = "*" [[package]] name = "openai" -version = "1.9.0" +version = "1.10.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.9.0-py3-none-any.whl", hash = "sha256:5774a0582ed82f6de92200ed5024e03e272b93e04e9d31caeda5fb80f63df50d"}, - {file = "openai-1.9.0.tar.gz", hash = "sha256:3e9947a544556c051fa138a4def5bd8b468364ec52803c6628532ab949ddce55"}, + {file = "openai-1.10.0-py3-none-any.whl", hash = "sha256:aa69e97d0223ace9835fbf9c997abe9ee95318f684fd2de6d02c870700c71ebc"}, + {file = "openai-1.10.0.tar.gz", hash = "sha256:208886cb501b930dc63f48d51db9c15e5380380f80516d07332adad67c9f1053"}, ] [package.dependencies] @@ -2564,13 +2564,13 @@ files = [ [[package]] name = "overrides" -version = "7.6.0" +version = "7.7.0" description = "A decorator to automatically detect mismatch when overriding a method." optional = false python-versions = ">=3.6" files = [ - {file = "overrides-7.6.0-py3-none-any.whl", hash = "sha256:c36e6635519ea9c5b043b65c36d4b886aee8bd45b7d4681d2a6df0898df4b654"}, - {file = "overrides-7.6.0.tar.gz", hash = "sha256:01e15bbbf15b766f0675c275baa1878bd1c7dc9bc7b9ee13e677cdba93dc1bd9"}, + {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, + {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, ] [[package]] @@ -2658,97 +2658,103 @@ files = [ [[package]] name = "pillow" -version = "9.5.0" +version = "10.2.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, - {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, - {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, - {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, - {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, - {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, - {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, - {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, - {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, - {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, - {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, - {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, - {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, - {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, - {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, - {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, - {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, + {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, + {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, + {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, + {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, + {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, + {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, + {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, + {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, + {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, + {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, + {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, + {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, + {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, + {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, + {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, + {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" @@ -2767,13 +2773,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "posthog" -version = "3.3.2" +version = "3.3.4" description = "Integrate PostHog into any python application." optional = false python-versions = "*" files = [ - {file = "posthog-3.3.2-py2.py3-none-any.whl", hash = "sha256:14fb43ea95c40b353db59c49af2c09ff15188aa2963f48091fc7912fa9375263"}, - {file = "posthog-3.3.2.tar.gz", hash = "sha256:734bf89f3c372605a8bbf2b07f600885287209145d747b09ccd004c59834750e"}, + {file = "posthog-3.3.4-py2.py3-none-any.whl", hash = "sha256:2fec5112c6df1d6a214a899e409659ed354511236537e861f1556a0c88e3fd26"}, + {file = "posthog-3.3.4.tar.gz", hash = "sha256:23a891639bc0a4f6fe4d04864d02410c60b7ee5d523de79becbc7325c983dba9"}, ] [package.dependencies] @@ -3012,18 +3018,18 @@ files = [ [[package]] name = "pydantic" -version = "2.5.3" +version = "2.6.0" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"}, + {file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.6" +pydantic-core = "2.16.1" typing-extensions = ">=4.6.1" [package.extras] @@ -3031,116 +3037,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.6" +version = "2.16.1" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, - {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, - {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, - {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, - {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, - {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, - {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, - {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, - {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, - {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, - {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, - {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"}, + {file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"}, + {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"}, + {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"}, + {file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"}, + {file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"}, + {file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"}, + {file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"}, + {file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"}, + {file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"}, + {file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"}, + {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"}, + {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"}, + {file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"}, + {file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"}, + {file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"}, + {file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"}, + {file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"}, + {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"}, + {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"}, + {file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"}, + {file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"}, + {file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"}, + {file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"}, + {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"}, + {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"}, + {file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"}, + {file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"}, + {file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"}, ] [package.dependencies] @@ -3148,20 +3128,20 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-extra-types" -version = "2.4.1" +version = "2.5.0" description = "Extra Pydantic types." optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_extra_types-2.4.1-py3-none-any.whl", hash = "sha256:b3cec735e471b1234a1cc05a680dc080836bab6970cab40d60dcade97fe68f5d"}, - {file = "pydantic_extra_types-2.4.1.tar.gz", hash = "sha256:63314096ca57bc1575d988d1a770d73af76aaebe684140f24333b60af4134a2c"}, + {file = "pydantic_extra_types-2.5.0-py3-none-any.whl", hash = "sha256:7346873019cac32061b471adf2cdac711664ddb7a6ede04219bed2da34888c4d"}, + {file = "pydantic_extra_types-2.5.0.tar.gz", hash = "sha256:46b85240093dc63ad4a8f3cab49e03d76ae0577e4f99e2bbff7d32f99d009bf9"}, ] [package.dependencies] pydantic = ">=2.5.2" [package.extras] -all = ["phonenumbers (>=8,<9)", "pycountry (>=23,<24)", "python-ulid (>=1,<2)"] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23,<24)", "python-ulid (>=1,<2)"] [[package]] name = "pydantic-settings" @@ -3637,17 +3617,17 @@ pyasn1 = ">=0.1.3" [[package]] name = "runpod" -version = "1.5.2" +version = "1.6.0" description = "🐍 | Python library for RunPod API and serverless worker SDK." optional = false python-versions = ">=3.8" files = [ - {file = "runpod-1.5.2-py3-none-any.whl", hash = "sha256:4b60a6bda6397ad912d58131af0575fad15877d041f0f170e2bee64901ab659a"}, - {file = "runpod-1.5.2.tar.gz", hash = "sha256:f37ceac64809e1c969ca460d01c48df12c9d60693fe1d89a1909283225efd4e2"}, + {file = "runpod-1.6.0-py3-none-any.whl", hash = "sha256:e445419fddbf10f728ea1d7535bf1cdb1932fdfe5d6e1d7a3a74215c4a00c86d"}, + {file = "runpod-1.6.0.tar.gz", hash = "sha256:a10cfda55336c5b60b27a7cd2b1e24b3685b8cd5406838a8ccbcd3beb7e8a35e"}, ] [package.dependencies] -aiohttp = {version = "3.9.1", extras = ["speedups"]} +aiohttp = {version = "3.9.2", extras = ["speedups"]} aiohttp-retry = ">=2.8.3" backoff = ">=2.2.1" boto3 = ">=1.26.165" @@ -3923,25 +3903,26 @@ test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", [[package]] name = "sentence-transformers" -version = "2.2.2" +version = "2.3.1" description = "Multilingual text embeddings" optional = false -python-versions = ">=3.6.0" +python-versions = ">=3.8.0" files = [ - {file = "sentence-transformers-2.2.2.tar.gz", hash = "sha256:dbc60163b27de21076c9a30d24b5b7b6fa05141d68cf2553fa9a77bf79a29136"}, + {file = "sentence-transformers-2.3.1.tar.gz", hash = "sha256:d589d85a464f45338cdbdf99ea715f8068e1fb01c582e0bcdbf60bcf3eade6d0"}, + {file = "sentence_transformers-2.3.1-py3-none-any.whl", hash = "sha256:285d6637726c3b002186aa4b8bcace1101364b32671fb605297c4c2636b8190e"}, ] [package.dependencies] -huggingface-hub = ">=0.4.0" +huggingface-hub = ">=0.15.1" nltk = "*" numpy = "*" +Pillow = "*" scikit-learn = "*" scipy = "*" sentencepiece = "*" -torch = ">=1.6.0" -torchvision = "*" +torch = ">=1.11.0" tqdm = "*" -transformers = ">=4.6.0,<5.0.0" +transformers = ">=4.32.0,<5.0.0" [[package]] name = "sentencepiece" @@ -4347,31 +4328,36 @@ files = [ [[package]] name = "torch" -version = "2.1.2" +version = "2.2.0" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" optional = false python-versions = ">=3.8.0" files = [ - {file = "torch-2.1.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:3a871edd6c02dae77ad810335c0833391c1a4ce49af21ea8cf0f6a5d2096eea8"}, - {file = "torch-2.1.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:bef6996c27d8f6e92ea4e13a772d89611da0e103b48790de78131e308cf73076"}, - {file = "torch-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:0e13034fd5fb323cbbc29e56d0637a3791e50dd589616f40c79adfa36a5a35a1"}, - {file = "torch-2.1.2-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:d9b535cad0df3d13997dbe8bd68ac33e0e3ae5377639c9881948e40794a61403"}, - {file = "torch-2.1.2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:f9a55d55af02826ebfbadf4e9b682f0f27766bc33df8236b48d28d705587868f"}, - {file = "torch-2.1.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:a6ebbe517097ef289cc7952783588c72de071d4b15ce0f8b285093f0916b1162"}, - {file = "torch-2.1.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:8f32ce591616a30304f37a7d5ea80b69ca9e1b94bba7f308184bf616fdaea155"}, - {file = "torch-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e0ee6cf90c8970e05760f898d58f9ac65821c37ffe8b04269ec787aa70962b69"}, - {file = "torch-2.1.2-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:76d37967c31c99548ad2c4d3f2cf191db48476f2e69b35a0937137116da356a1"}, - {file = "torch-2.1.2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:e2d83f07b4aac983453ea5bf8f9aa9dacf2278a8d31247f5d9037f37befc60e4"}, - {file = "torch-2.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f41fe0c7ecbf903a568c73486139a75cfab287a0f6c17ed0698fdea7a1e8641d"}, - {file = "torch-2.1.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e3225f47d50bb66f756fe9196a768055d1c26b02154eb1f770ce47a2578d3aa7"}, - {file = "torch-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:33d59cd03cb60106857f6c26b36457793637512998666ee3ce17311f217afe2b"}, - {file = "torch-2.1.2-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:8e221deccd0def6c2badff6be403e0c53491805ed9915e2c029adbcdb87ab6b5"}, - {file = "torch-2.1.2-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:05b18594f60a911a0c4f023f38a8bda77131fba5fd741bda626e97dcf5a3dd0a"}, - {file = "torch-2.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:9ca96253b761e9aaf8e06fb30a66ee301aecbf15bb5a303097de1969077620b6"}, - {file = "torch-2.1.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d93ba70f67b08c2ae5598ee711cbc546a1bc8102cef938904b8c85c2089a51a0"}, - {file = "torch-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:255b50bc0608db177e6a3cc118961d77de7e5105f07816585fa6f191f33a9ff3"}, - {file = "torch-2.1.2-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:6984cd5057c0c977b3c9757254e989d3f1124f4ce9d07caa6cb637783c71d42a"}, - {file = "torch-2.1.2-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:bc195d7927feabc0eb7c110e457c955ed2ab616f3c7c28439dd4188cf589699f"}, + {file = "torch-2.2.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:d366158d6503a3447e67f8c0ad1328d54e6c181d88572d688a625fac61b13a97"}, + {file = "torch-2.2.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:707f2f80402981e9f90d0038d7d481678586251e6642a7a6ef67fc93511cb446"}, + {file = "torch-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:15c8f0a105c66b28496092fca1520346082e734095f8eaf47b5786bac24b8a31"}, + {file = "torch-2.2.0-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:0ca4df4b728515ad009b79f5107b00bcb2c63dc202d991412b9eb3b6a4f24349"}, + {file = "torch-2.2.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:3d3eea2d5969b9a1c9401429ca79efc668120314d443d3463edc3289d7f003c7"}, + {file = "torch-2.2.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:0d1c580e379c0d48f0f0a08ea28d8e373295aa254de4f9ad0631f9ed8bc04c24"}, + {file = "torch-2.2.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9328e3c1ce628a281d2707526b4d1080eae7c4afab4f81cea75bde1f9441dc78"}, + {file = "torch-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:03c8e660907ac1b8ee07f6d929c4e15cd95be2fb764368799cca02c725a212b8"}, + {file = "torch-2.2.0-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:da0cefe7f84ece3e3b56c11c773b59d1cb2c0fd83ddf6b5f7f1fd1a987b15c3e"}, + {file = "torch-2.2.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:f81d23227034221a4a4ff8ef24cc6cec7901edd98d9e64e32822778ff01be85e"}, + {file = "torch-2.2.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:dcbfb2192ac41ca93c756ebe9e2af29df0a4c14ee0e7a0dd78f82c67a63d91d4"}, + {file = "torch-2.2.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:9eeb42971619e24392c9088b5b6d387d896e267889d41d267b1fec334f5227c5"}, + {file = "torch-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:c718b2ca69a6cac28baa36d86d8c0ec708b102cebd1ceb1b6488e404cd9be1d1"}, + {file = "torch-2.2.0-cp312-none-macosx_10_9_x86_64.whl", hash = "sha256:f11d18fceb4f9ecb1ac680dde7c463c120ed29056225d75469c19637e9f98d12"}, + {file = "torch-2.2.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:ee1da852bfd4a7e674135a446d6074c2da7194c1b08549e31eae0b3138c6b4d2"}, + {file = "torch-2.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0d819399819d0862268ac531cf12a501c253007df4f9e6709ede8a0148f1a7b8"}, + {file = "torch-2.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:08f53ccc38c49d839bc703ea1b20769cc8a429e0c4b20b56921a9f64949bf325"}, + {file = "torch-2.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:93bffe3779965a71dab25fc29787538c37c5d54298fd2f2369e372b6fb137d41"}, + {file = "torch-2.2.0-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:c17ec323da778efe8dad49d8fb534381479ca37af1bfc58efdbb8607a9d263a3"}, + {file = "torch-2.2.0-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:c02685118008834e878f676f81eab3a952b7936fa31f474ef8a5ff4b5c78b36d"}, + {file = "torch-2.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d9f39d6f53cec240a0e3baa82cb697593340f9d4554cee6d3d6ca07925c2fac0"}, + {file = "torch-2.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:51770c065206250dc1222ea7c0eff3f88ab317d3e931cca2aee461b85fbc2472"}, + {file = "torch-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:008e4c6ad703de55af760c73bf937ecdd61a109f9b08f2bbb9c17e7c7017f194"}, + {file = "torch-2.2.0-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:de8680472dd14e316f42ceef2a18a301461a9058cd6e99a1f1b20f78f11412f1"}, + {file = "torch-2.2.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:99e1dcecb488e3fd25bcaac56e48cdb3539842904bdc8588b0b255fde03a254c"}, ] [package.dependencies] @@ -4388,53 +4374,15 @@ nvidia-cufft-cu12 = {version = "11.0.2.54", markers = "platform_system == \"Linu nvidia-curand-cu12 = {version = "10.3.2.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} nvidia-cusolver-cu12 = {version = "11.4.5.107", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} nvidia-cusparse-cu12 = {version = "12.1.0.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-nccl-cu12 = {version = "2.18.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.19.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} nvidia-nvtx-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} sympy = "*" -triton = {version = "2.1.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -typing-extensions = "*" +triton = {version = "2.2.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +typing-extensions = ">=4.8.0" [package.extras] -dynamo = ["jinja2"] opt-einsum = ["opt-einsum (>=3.3)"] - -[[package]] -name = "torchvision" -version = "0.16.2" -description = "image and video datasets and models for torch deep learning" -optional = false -python-versions = ">=3.8" -files = [ - {file = "torchvision-0.16.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:bc86f2800cb2c0c1a09c581409cdd6bff66e62f103dc83fc63f73346264c3756"}, - {file = "torchvision-0.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b024bd412df6d3a007dcebf311a894eb3c5c21e1af80d12be382bbcb097a7c3a"}, - {file = "torchvision-0.16.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:e89f10f3c8351972b6e3fda95bc3e479ea8dbfc9dfcfd2c32902dbad4ba5cfc5"}, - {file = "torchvision-0.16.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:96c7583700112a410bdc4e1e4f118c429dab49c29c9a31a2cc3579bc9b08b19d"}, - {file = "torchvision-0.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:9f4032ebb3277fb07ff6a9b818d50a547fb8fcd89d958cfd9e773322454bb688"}, - {file = "torchvision-0.16.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:67b1aaf8b8cb02ce75dd445f291a27c8036a502f8c0aa76e28c37a0faac2e153"}, - {file = "torchvision-0.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bef30d03e1d1c629761f4dca51d3b7d8a0dc0acce6f4068ab2a1634e8e7b64e0"}, - {file = "torchvision-0.16.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:e59cc7b2bd1ab5c0ce4ae382e4e37be8f1c174e8b5de2f6a23c170de9ae28495"}, - {file = "torchvision-0.16.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:e130b08cc9b3cc73a6c59d6edf032394a322f9579bfd21d14bc2e1d0999aa758"}, - {file = "torchvision-0.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:8692ab1e48807e9604046a6f4beeb67b523294cee1b00828654bb0df2cfce2b2"}, - {file = "torchvision-0.16.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:b82732dcf876a37c852772342aa6ee3480c03bb3e2a802ae109fc5f7e28d26e9"}, - {file = "torchvision-0.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4b065143d1a720fe8a9077fd4be35d491f98819ec80b3dbbc3ec64d0b707a906"}, - {file = "torchvision-0.16.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bc5f274e4ecd1b86062063cdf4fd385a1d39d147a3a2685fbbde9ff08bb720b8"}, - {file = "torchvision-0.16.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:335959c43b371c0474af34c1ef2a52efdc7603c45700d29e4475eeb02984170c"}, - {file = "torchvision-0.16.2-cp38-cp38-win_amd64.whl", hash = "sha256:7fd22d86e08eba321af70cad291020c2cdeac069b00ce88b923ca52e06174769"}, - {file = "torchvision-0.16.2-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:56115268b37f0b75364e3654e47ad9abc66ac34c1f9e5e3dfa89a22d6a40017a"}, - {file = "torchvision-0.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:82805f8445b094f9d1e770390ee6cc86855e89955e08ce34af2e2274fc0e5c45"}, - {file = "torchvision-0.16.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3f4bd5fcbc361476e2e78016636ac7d5509e59d9962521f06eb98e6803898182"}, - {file = "torchvision-0.16.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8199acdf8ab066a28b84a5b6f4d97b58976d9e164b1acc3a9d14fccfaf74bb3a"}, - {file = "torchvision-0.16.2-cp39-cp39-win_amd64.whl", hash = "sha256:41dd4fa9f176d563fe9f1b9adef3b7e582cdfb60ce8c9bc51b094a025be687c9"}, -] - -[package.dependencies] -numpy = "*" -pillow = ">=5.3.0,<8.3.dev0 || >=8.4.dev0" -requests = "*" -torch = "2.1.2" - -[package.extras] -scipy = ["scipy"] +optree = ["optree (>=0.9.1)"] [[package]] name = "tqdm" @@ -4472,13 +4420,13 @@ tqdm = ">4.64" [[package]] name = "transformers" -version = "4.37.1" +version = "4.37.2" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = false python-versions = ">=3.8.0" files = [ - {file = "transformers-4.37.1-py3-none-any.whl", hash = "sha256:05e4c4bf94f74addeb716bc83517f49d55df1e9022db3d5b027c801e9a410ebf"}, - {file = "transformers-4.37.1.tar.gz", hash = "sha256:9843368d97fd7ac30126664743adc65e8e5be930da7d66342172e97bd1243e2d"}, + {file = "transformers-4.37.2-py3-none-any.whl", hash = "sha256:595a8b12a1fcc4ad0ced49ce206c58e17be68c85d7aee3d7546d04a32c910d2e"}, + {file = "transformers-4.37.2.tar.gz", hash = "sha256:f307082ae5d528b8480611a4879a4a11651012d0e9aaea3f6cf17219ffd95542"}, ] [package.dependencies] @@ -4489,7 +4437,7 @@ packaging = ">=20.0" pyyaml = ">=5.1" regex = "!=2019.12.17" requests = "*" -safetensors = ">=0.3.1" +safetensors = ">=0.4.1" tokenizers = ">=0.14,<0.19" tqdm = ">=4.27" @@ -4540,28 +4488,26 @@ vision = ["Pillow (>=10.0.1,<=15.0)"] [[package]] name = "triton" -version = "2.1.0" +version = "2.2.0" description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "*" files = [ - {file = "triton-2.1.0-0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:66439923a30d5d48399b08a9eae10370f6c261a5ec864a64983bae63152d39d7"}, - {file = "triton-2.1.0-0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:919b06453f0033ea52c13eaf7833de0e57db3178d23d4e04f9fc71c4f2c32bf8"}, - {file = "triton-2.1.0-0-cp37-cp37m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae4bb8a91de790e1866405211c4d618379781188f40d5c4c399766914e84cd94"}, - {file = "triton-2.1.0-0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39f6fb6bdccb3e98f3152e3fbea724f1aeae7d749412bbb1fa9c441d474eba26"}, - {file = "triton-2.1.0-0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21544e522c02005a626c8ad63d39bdff2f31d41069592919ef281e964ed26446"}, - {file = "triton-2.1.0-0-pp37-pypy37_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:143582ca31dd89cd982bd3bf53666bab1c7527d41e185f9e3d8a3051ce1b663b"}, - {file = "triton-2.1.0-0-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82fc5aeeedf6e36be4e4530cbdcba81a09d65c18e02f52dc298696d45721f3bd"}, - {file = "triton-2.1.0-0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81a96d110a738ff63339fc892ded095b31bd0d205e3aace262af8400d40b6fa8"}, + {file = "triton-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2294514340cfe4e8f4f9e5c66c702744c4a117d25e618bd08469d0bfed1e2e5"}, + {file = "triton-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da58a152bddb62cafa9a857dd2bc1f886dbf9f9c90a2b5da82157cd2b34392b0"}, + {file = "triton-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af58716e721460a61886668b205963dc4d1e4ac20508cc3f623aef0d70283d5"}, + {file = "triton-2.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8fe46d3ab94a8103e291bd44c741cc294b91d1d81c1a2888254cbf7ff846dab"}, + {file = "triton-2.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ce26093e539d727e7cf6f6f0d932b1ab0574dc02567e684377630d86723ace"}, + {file = "triton-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:227cc6f357c5efcb357f3867ac2a8e7ecea2298cd4606a8ba1e931d1d5a947df"}, ] [package.dependencies] filelock = "*" [package.extras] -build = ["cmake (>=3.18)", "lit"] -tests = ["autopep8", "flake8", "isort", "numpy", "pytest", "scipy (>=1.7.1)"] -tutorials = ["matplotlib", "pandas", "tabulate"] +build = ["cmake (>=3.20)", "lit"] +tests = ["autopep8", "flake8", "isort", "numpy", "pytest", "scipy (>=1.7.1)", "torch"] +tutorials = ["matplotlib", "pandas", "tabulate", "torch"] [[package]] name = "typer" @@ -5203,4 +5149,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "b18d1732d7affd7327d5db3705287d1e4e7bfe392cb3ed6898769efc627e747c" +content-hash = "db482d7b3917ef31d8e1b49e7fc860e2b40e10ab9e8f0a536e9fab27c7bd2261" diff --git a/pyproject.toml b/pyproject.toml index a868dd80..ed08f572 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api" [tool.poetry] name = "talemate" -version = "0.18.2" +version = "0.19.0" description = "AI-backed roleplay and narrative tools" authors = ["FinalWombat"] license = "GNU Affero General Public License v3.0" @@ -20,7 +20,7 @@ jinja2 = "^3.0" openai = ">=1" requests = "^2.26" colorama = ">=0.4.6" -Pillow = "^9.5" +Pillow = ">=9.5" httpx = "<1" piexif = "^1.1" typing-inspect = "0.8.0" diff --git a/scenes/infinity-quest-dynamic-scenario/assets/e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404.png b/scenes/infinity-quest-dynamic-scenario/assets/e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404.png new file mode 100644 index 00000000..9c328adc Binary files /dev/null and b/scenes/infinity-quest-dynamic-scenario/assets/e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404.png differ diff --git a/scenes/infinity-quest-dynamic-scenario/infinity-quest.json b/scenes/infinity-quest-dynamic-scenario/infinity-quest.json index 4ddd9236..c571c1cb 100644 --- a/scenes/infinity-quest-dynamic-scenario/infinity-quest.json +++ b/scenes/infinity-quest-dynamic-scenario/infinity-quest.json @@ -98,6 +98,7 @@ } ], "immutable_save": true, + "experimental": true, "goal": null, "goals": [], "context": "an epic sci-fi adventure aimed at an adult audience.", @@ -109,10 +110,10 @@ "variables": {} }, "assets": { - "cover_image": "52b1388ed6f77a43981bd27e05df54f16e12ba8de1c48f4b9bbcb138fa7367df", + "cover_image": "e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404", "assets": { - "52b1388ed6f77a43981bd27e05df54f16e12ba8de1c48f4b9bbcb138fa7367df": { - "id": "52b1388ed6f77a43981bd27e05df54f16e12ba8de1c48f4b9bbcb138fa7367df", + "e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404": { + "id": "e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404", "file_type": "png", "media_type": "image/png" } diff --git a/scenes/infinity-quest-dynamic-scenario/templates/instructions.jinja2 b/scenes/infinity-quest-dynamic-scenario/templates/instructions.jinja2 index e720ef14..515c1572 100644 --- a/scenes/infinity-quest-dynamic-scenario/templates/instructions.jinja2 +++ b/scenes/infinity-quest-dynamic-scenario/templates/instructions.jinja2 @@ -5,7 +5,7 @@ {%- set _ = emit_system("warning", "This is a dynamic scenario generation experiment for Infinity Quest. It will likely require a strong LLM to generate something coherent. GPT-4 or 34B+ if local. Temper your expectations.") -%} {#- emit status update to the UX -#} - {%- set _ = emit_status("busy", "Generating scenario ... [1/3]") -%} + {%- set _ = emit_status("busy", "Generating scenario ... [1/3]", as_scene_message=True) -%} {#- thematic tags will be used to randomize generation -#} {%- set tags = thematic_generator.generate("color", "state_of_matter", "scifi_trope") -%} @@ -17,17 +17,17 @@ {#- generate introductory text -#} - {%- set _ = emit_status("busy", "Generating scenario ... [2/3]") -%} + {%- set _ = emit_status("busy", "Generating scenario ... [2/3]", as_scene_message=True) -%} {%- set tmpl__scenario_intro = render_template('generate-scenario-intro', premise=instr__premise) %} {%- set instr__intro = "*"+render_and_request(tmpl__scenario_intro)+"*" -%} {#- generate win conditions -#} - {%- set _ = emit_status("busy", "Generating scenario ... [3/3]") -%} + {%- set _ = emit_status("busy", "Generating scenario ... [3/3]", as_scene_message=True) -%} {%- set tmpl__win_conditions = render_template('generate-win-conditions', premise=instr__premise) %} {%- set instr__win_conditions = render_and_request(tmpl__win_conditions) -%} {#- emit status update to the UX -#} - {%- set status = emit_status("info", "Scenario ready.") -%} + {%- set status = emit_status("success", "Scenario ready.", as_scene_message=True) -%} {# set gamestate variables #} {%- set _ = game_state.set_var("instr.premise", instr__premise, commit=True) -%} diff --git a/scenes/simulation-suite/assets/4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103.png b/scenes/simulation-suite/assets/4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103.png new file mode 100644 index 00000000..56c803b5 Binary files /dev/null and b/scenes/simulation-suite/assets/4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103.png differ diff --git a/scenes/simulation-suite/simulation-suite.json b/scenes/simulation-suite/simulation-suite.json new file mode 100644 index 00000000..7fe717d7 --- /dev/null +++ b/scenes/simulation-suite/simulation-suite.json @@ -0,0 +1,52 @@ +{ + "name": "Simulation Suite", + "environment": "scene", + "immutable_save": true, + "restore_from": "simulation-suite.json", + "experimental": true, + "help": "Address the computer by starting your statements with 'Computer, ' followed by an instruction.\n\nExamples:\n'Computer, i would like to experience an adventure on a derelict space station'\n'Computer, add a horrific alien creature that is chasing me.'", + "description": "", + "intro": "*You have entered the simulation suite. No simulation is currently active and you are in a non-descript space with paneled walls surrounding you. The control panel next to you is pulsating with a green light, indicating readiness to receive a prompt to start the simulation.*", + "archived_history": [], + "history": [], + "ts": "PT1S", + "characters": [ + { + "name": "You", + "gender": "unknown", + "color": "cornflowerblue", + "base_attributes": {}, + "is_player": true + } + ], + "context": "a simulated experience", + "game_state": { + "ops":{ + "run_on_start": true, + "always_direct": true + }, + "variables": {} + }, + "world_state": { + "character_name_mappings": { + "You": [ + "user", + "player", + "player character", + "user character", + "the user", + "the player" + ] + } + }, + "assets": { + "cover_image": "4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103", + "assets": { + "4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103": { + "id": "4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103", + "file_type": "png", + "media_type": "image/png" + } + } + } +} \ No newline at end of file diff --git a/scenes/simulation-suite/templates/computer.jinja2 b/scenes/simulation-suite/templates/computer.jinja2 new file mode 100644 index 00000000..328130d2 --- /dev/null +++ b/scenes/simulation-suite/templates/computer.jinja2 @@ -0,0 +1,110 @@ +<|SECTION:CONTEXT|> +{% set scene_history=scene.context_history(budget=1024) %} +{% for scene_context in scene_history -%} +{{ loop.index }}. {{ scene_context }} +{% endfor %} +<|CLOSE_SECTION|> +<|SECTION:FUNCTIONS|> +The player has instructed the computer to alter the current simulation. + +You have access to the following functions, you can call as many as you want to fulfill the player's requests. + +You must at least call one of the following functions: + +- change_environment +- add_ai_character +- change_ai_character +- remove_ai_character +- set_player_persona +- set_player_name +- end_simulation + +Set the player persona at the beginning of a new simulation or if the player requests a change. + +Only end the simulation if the player requests it explicitly. +<|CLOSE_SECTION|> +<|SECTION:EXAMPLES|> +Request: Computer, I want to be on a mountain top +```simulation-stack +change_environment("mountain top") +set_player_persona("mountain climber") +set_player_name("Hank") +``` + +Request: Computer, I want to be more muscular and taller +```simulation-stack +set_player_persona("make player more muscular and taller") +``` + +Request: Computer, the building should be on fire +```simulation-stack +change_environment("building on fire") +``` + +Request: Computer, a rocket hits the building and George is now injured +```simulation-stack +change_environment("building on fire") +change_ai_character("George is injured") +``` + +Request: Computer, I want to experience a rollercoaster ride with a friend +```simulation-stack +change_environment("theme park, riding a rollercoaster") +set_player_persona("young female experiencing rollercoaster ride") +set_player_name("Susanne") +add_ai_character("a female friend of player") +``` + +Request: Computer, I want to experience the international space station +```simulation-stack +change_environment("international space station") +set_player_persona("astronaut experiencing first trip to ISS") +set_player_name("George") +add_ai_character("astronaut") +``` + +Request: Computer, remove the goblin and add an elven woman instead +```simulation-stack +remove_ai_character("goblin") +add_ai_character("elven woman named Elune") +``` + +Request: Computer, change the skiing instructor to be older. +```simulation-stack +change_ai_character("make skiing instructor older") +``` + +Request: Computer, change my grandma to my grandpa +```simulation-stack +remove_ai_character("grandma") +add_ai_character("grandpa") +``` + +Request: Computer, remove the skiing instructor and add my friend instead. +```simulation-stack +remove_ai_character("skiing instructor") +add_ai_character("player's friend") +``` + +Request: Computer, replace the skiing instructor with my friend. +```simulation-stack +remove_ai_character("skiing instructor") +add_ai_character("player's friend") +``` + +Request: Computer, I want to end the simulation +```simulation-stack +end_simulation("simulation ended") +``` + +Request: Computer, shut down the simulation +```simulation-stack +end_simulation("simulation ended") +``` + +<|CLOSE_SECTION|> +<|SECTION:TASK|> +Respond with the simulation stack for the following request: + +Request: {{ player_instruction }} +{{ bot_token }}```simulation-stack \ No newline at end of file diff --git a/scenes/simulation-suite/templates/instructions.jinja2 b/scenes/simulation-suite/templates/instructions.jinja2 new file mode 100644 index 00000000..3e3bb40e --- /dev/null +++ b/scenes/simulation-suite/templates/instructions.jinja2 @@ -0,0 +1,165 @@ +{% set update_world_state = False %} +{% set _ = debug("HOLODECK SIMULATION") -%} +{% set player_character = scene.get_player_character() %} +{% set player_message = scene.last_player_message() %} +{% set last_processed = game_state.get_var('instr.last_processed', -1) %} +{% set player_message_is_instruction = (player_message and player_message.raw.lower().startswith("computer") and not player_message.hidden) and not player_message.raw.lower().strip() == "computer" and not last_processed >= player_message.id %} +{% set simulation_reset = False %} +{% if not game_state.has_var('instr.simulation_stopped') %} +{# simulation NOT started #} + + {# get last player instruction #} + {% if player_message_is_instruction %} + {# player message exists #} + + {#% set _ = agent_action("narrator", "action_to_narration", action_name="paraphrase", narration="The computer is processing the request, please wait a moment.", emit_message=True) %#} + + {% set calls = render_and_request(render_template("computer", player_instruction=player_message.raw), dedupe_enabled=False) %} + + {% set _ = debug("HOLODECK simulation calls", calls=calls ) %} + {% set processed = make_list() %} + + {% for call in calls.split("\n") %} + {% set _ = debug("CALL", call=call, processed=processed) %} + {% set inject = "The computer executes the function `"+call+"`" %} + {% if call.strip().startswith('change_environment') %} + {# change environment #} + {% set _ = processed.append(call) %} + + {% elif call.strip().startswith("set_player_persona") %} + {# treansform player #} + {% set _ = emit_status("busy", "Simulation suite altering user persona.", as_scene_message=True) %} + + {% set character_attributes = agent_action("world_state", "extract_character_sheet", name=player_character.name, text=player_message.raw)%} + + {% set _ = player_character.update(base_attributes=character_attributes) %} + + {% set character_description = agent_action("creator", "determine_character_description", character=player_character) %} + + {% set _ = player_character.update(description=character_description) %} + + {% set _ = debug("HOLODECK transform player", attributes=character_attributes, description=character_description) %} + {% set _ = processed.append(call) %} + {% elif call.strip().startswith("set_player_name") %} + {# change player name #} + {% set _ = emit_status("busy", "Simulation suite adjusting user idenity.", as_scene_message=True) %} + {% set character_name = agent_action("creator", "determine_character_name", character_name=inject+" - What is a fitting name for the player persona? Respond with the current name if it still fits.") %} + + {% set _ = debug("HOLODECK player name", character_name=character_name) %} + + {% if character_name != player_character.name %} + {% set _ = processed.append(call) %} + {% set _ = player_character.rename(character_name) %} + {% endif %} + {% elif call.strip().startswith("add_ai_character") %} + {# add new npc #} + + {% set _ = emit_status("busy", "Simulation suite adding character.", as_scene_message=True) %} + {% set character_name = agent_action("creator", "determine_character_name", character_name=inject+" - what is the name of the character to be added to the scene? If no name can extracted from the text, extract a short descriptive name instead. Respond only with the name.") %} + + {% set _ = emit_status("busy", "Simulation suite adding character: "+character_name, as_scene_message=True) %} + {% set _ = debug("HOLODECK add npc", name=character_name)%} + {% set npc = agent_action("director", "persist_character", name=character_name, content=player_message.raw )%} + {% set _ = agent_action("world_state", "manager", action_name="add_detail_reinforcement", character_name=npc.name, question="Goal", instructions="Generate a goal for the character, based on the user's chosen simulation", interval=25, run_immediately=True) %} + {% set _ = debug("HOLODECK added npc", npc=npc) %} + {% set _ = processed.append(call) %} + {% elif call.strip().startswith("remove_ai_character") %} + {# remove npc #} + + {% set _ = emit_status("busy", "Simulation suite removing character.", as_scene_message=True) %} + {% set character_name = agent_action("creator", "determine_character_name", character_name=inject+" - what is the name of the character being removed?", allowed_names=scene.npc_character_names) %} + + {% set npc = scene.get_character(character_name) %} + + {% if npc %} + {% set _ = debug("HOLODECK remove npc", npc=npc.name) %} + {% set _ = agent_action("world_state", "manager", action_name="deactivate_character", character_name=npc.name) %} + {% set _ = processed.append(call) %} + {% endif %} + {% elif call.strip().startswith("change_ai_character") %} + {# change existing npc #} + + {% set _ = emit_status("busy", "Simulation suite altering character.", as_scene_message=True) %} + {% set character_name = agent_action("creator", "determine_character_name", character_name=inject+" - what is the name of the character receiving the changes?", allowed_names=scene.npc_character_names) %} + + {% set npc = scene.get_character(character_name) %} + + {% if npc %} + {% set _ = emit_status("busy", "Changing "+character_name, as_scene_message=True) %} + {% set _ = debug("HOLODECK transform npc", npc=npc) %} + {% set character_attributes = agent_action("world_state", "extract_character_sheet", name=npc.name, alteration_instructions=player_message.raw)%} + {% set _ = npc.update(base_attributes=character_attributes) %} + {% set character_description = agent_action("creator", "determine_character_description", character=npc) %} + {% set _ = npc.update(description=character_description) %} + {% set _ = debug("HOLODECK transform npc", attributes=character_attributes, description=character_description) %} + {% set _ = processed.append(call) %} + {% endif %} + {% elif call.strip().startswith("end_simulation") %} + {# end simulation #} + {% set explicit_command = query_text_eval("has the player explicitly asked to end the simulation?", player_message.raw) %} + {% if explicit_command %} + {% set _ = emit_status("busy", "Simulation suite ending current simulation.", as_scene_message=True) %} + {% set _ = agent_action("narrator", "action_to_narration", action_name="progress_story", narrative_direction="The computer ends the simulation, disolving the environment and all artifical characters, erasing all memory of it and finally returning the player to the inactive simulation suite.", emit_message=True) %} + {% set _ = scene.sync_restore() %} + {% set update_world_state = True %} + {% set simulation_reset = True %} + {% endif %} + {% elif "(" in call.strip() %} + {# unknown function call, still add it to processed stack so it can be incoorporated in the narration #} + {% set _ = processed.append(call) %} + {% endif %} + {% endfor %} + + {% if processed and not simulation_reset %} + {% set _ = game_state.set_var("instr.has_issued_instructions", "yes", commit=False) %} + {% set _ = emit_status("busy", "Simulation suite altering environment.", as_scene_message=True) %} + {% set update_world_state = True %} + {% set _ = agent_action("narrator", "action_to_narration", action_name="progress_story", narrative_direction="The computer calls the following functions:\n"+processed.join("\n")+"\nand the simulation adjusts the environment according to the user's wishes. Write the narrative that describes the changes.", emit_message=True) %} + {% endif %} + + {% elif not game_state.has_var("instr.simulation_started") %} + {# no player message yet, start of scenario #} + {% set _ = emit_status("busy", "Simulation suite powering up.", as_scene_message=True) %} + {% set _ = game_state.set_var("instr.simulation_started", "yes", commit=False) %} + {% set _ = agent_action("narrator", "action_to_narration", action_name="progress_story", narrative_direction="Narrate the computer asking the user to state the nature of their desired simulation.", emit_message=False) %} + {% set _ = agent_action("narrator", "action_to_narration", action_name="paraphrase", narration="Please state your commands by addressing the computer by stating \"Computer,\" followed by an instruction.") %} + + {# pin to make sure characters don't try to interact with the simulation #} + {% set _ = agent_action("world_state", "manager", action_name="save_world_entry", entry_id="sim.quarantined", text="Characters in the simulation ARE NOT AWARE OF THE COMPUTER.", meta=make_dict(), pin=True) %} + + {% set _ = emit_status("success", "Simulation suite ready", as_scene_message=True) %} + {% endif %} + +{% else %} +{# simulation ongoing #} + +{% endif %} + +{% if update_world_state %} + {% set _ = emit_status("busy", "Simulation suite updating world state.", as_scene_message=True) %} + {% set _ = agent_action("world_state", "update_world_state", force=True) %} +{% endif %} + +{% if not scene.npc_character_names and not simulation_reset %} + {# no characters in the scene, see if there are any to add #} + {% set npcs = agent_action("director", "persist_characters_from_worldstate", exclude=["computer", "user", "player", "you"]) %} + {% for npc in npcs %} + {% set _ = agent_action("world_state", "manager", action_name="add_detail_reinforcement", character_name=npc.name, question="Goal", instructions="Generate a goal for the character, based on the user's chosen simulation", interval=25, run_immediately=True) %} + {% endfor %} + {% if npcs %} + {% set _ = agent_action("world_state", "update_world_state", force=True) %} + {% endif %} +{% endif %} + +{% if player_message_is_instruction %} + {# hide player message to the computer, so its not included in the scene context #} + {% set _ = player_message.hide() %} + {% set _ = game_state.set_var("instr.last_processed", player_message.id, commit=False) %} + {% set _ = emit_status("success", "Simulation suite processed instructions", as_scene_message=True) %} +{% elif player_message and not game_state.has_var("instr.has_issued_instructions") %} + {# simulation not started, but player message is not an instruction #} + {% set _ = agent_action("narrator", "action_to_narration", action_name="paraphrase", narration="Instructions to the simulation computer are only process if the computer is addressed at the beginning of the instruction. Please state your commands by addressing the computer by stating \"Computer,\" followed by an instruction. For example ... \"Computer, i want to experience being on a derelict spaceship.\"", emit_message=True) %} +{% elif player_message and not scene.npc_character_names %} + {# simulation started, player message is NOT an instruction, but there are no npcs to interact with #} + {% set _ = agent_action("narrator", "action_to_narration", action_name="progress_story", narrative_direction="The environment reacts to the player's actions. YOU MUST NOT ACT ON BEHALF OF THE PLAYER. YOU MUST NOT INTERACT WITH THE COMPUTER.", emit_message=True) %} +{% endif %} \ No newline at end of file diff --git a/src/talemate/__init__.py b/src/talemate/__init__.py index b13a1184..ee84461f 100644 --- a/src/talemate/__init__.py +++ b/src/talemate/__init__.py @@ -2,4 +2,4 @@ from .agents import Agent from .client import TextGeneratorWebuiClient from .tale_mate import * -VERSION = "0.18.2" +VERSION = "0.19.0" diff --git a/src/talemate/agents/base.py b/src/talemate/agents/base.py index f561385b..b8b2e7c7 100644 --- a/src/talemate/agents/base.py +++ b/src/talemate/agents/base.py @@ -4,6 +4,7 @@ import asyncio import dataclasses import re from abc import ABC +from functools import wraps from typing import TYPE_CHECKING, Callable, List, Optional, Union import pydantic @@ -58,6 +59,7 @@ def set_processing(fn): the function fails. """ + @wraps(fn) async def wrapper(self, *args, **kwargs): with ActiveAgent(self, fn): try: @@ -71,8 +73,6 @@ def set_processing(fn): # some concurrency error? log.error("error emitting agent status", exc=exc) - wrapper.__name__ = fn.__name__ - return wrapper diff --git a/src/talemate/agents/context.py b/src/talemate/agents/context.py index 9fb5bf34..b4896151 100644 --- a/src/talemate/agents/context.py +++ b/src/talemate/agents/context.py @@ -13,6 +13,7 @@ active_agent = contextvars.ContextVar("active_agent", default=None) class ActiveAgentContext(pydantic.BaseModel): agent: object fn: Callable + agent_stack: list = pydantic.Field(default_factory=list) class Config: arbitrary_types_allowed = True @@ -21,12 +22,23 @@ class ActiveAgentContext(pydantic.BaseModel): def action(self): return self.fn.__name__ + def __str__(self): + return f"{self.agent.verbose_name}.{self.action}" + class ActiveAgent: def __init__(self, agent, fn): self.agent = ActiveAgentContext(agent=agent, fn=fn) def __enter__(self): + + previous_agent = active_agent.get() + + if previous_agent: + self.agent.agent_stack = previous_agent.agent_stack + [str(self.agent)] + else: + self.agent.agent_stack = [str(self.agent)] + self.token = active_agent.set(self.agent) def __exit__(self, *args, **kwargs): diff --git a/src/talemate/agents/conversation.py b/src/talemate/agents/conversation.py index a3efb3c4..4ab6ec6f 100644 --- a/src/talemate/agents/conversation.py +++ b/src/talemate/agents/conversation.py @@ -85,7 +85,7 @@ class ConversationAgent(Agent): type="number", label="Generation Length (tokens)", description="Maximum number of tokens to generate for a conversation response.", - value=96, + value=128, min=32, max=512, step=32, @@ -660,4 +660,4 @@ class ConversationAgent(Agent): ): if prompt_param.get("extra_stopping_strings") is None: prompt_param["extra_stopping_strings"] = [] - prompt_param["extra_stopping_strings"] += ["["] + prompt_param["extra_stopping_strings"] += ["#"] diff --git a/src/talemate/agents/creator/__init__.py b/src/talemate/agents/creator/__init__.py index 07358d4a..5954717e 100644 --- a/src/talemate/agents/creator/__init__.py +++ b/src/talemate/agents/creator/__init__.py @@ -9,13 +9,13 @@ from talemate.agents.registry import register from talemate.emit import emit from talemate.prompts import Prompt +from .assistant import AssistantMixin from .character import CharacterCreatorMixin from .scenario import ScenarioCreatorMixin @register() -class CreatorAgent(CharacterCreatorMixin, ScenarioCreatorMixin, Agent): - +class CreatorAgent(CharacterCreatorMixin, ScenarioCreatorMixin, AssistantMixin, Agent): """ Creates characters and scenarios and other fun stuff! """ diff --git a/src/talemate/agents/creator/assistant.py b/src/talemate/agents/creator/assistant.py new file mode 100644 index 00000000..0ece7004 --- /dev/null +++ b/src/talemate/agents/creator/assistant.py @@ -0,0 +1,95 @@ +from typing import TYPE_CHECKING, Union + +import pydantic + +import talemate.util as util +from talemate.agents.base import set_processing +from talemate.prompts import Prompt + +if TYPE_CHECKING: + from talemate.tale_mate import Character, Scene + + +class ContentGenerationContext(pydantic.BaseModel): + """ + A context for generating content. + """ + + context: str + instructions: str + length: int + character: Union[str, None] = None + original: Union[str, None] = None + + @property + def computed_context(self) -> (str, str): + typ, context = self.context.split(":", 1) + return typ, context + + +class AssistantMixin: + """ + Creator mixin that allows quick contextual generation of content. + """ + + async def contextual_generate_from_args( + self, + context: str, + instructions: str, + length: int = 100, + character: Union[str, None] = None, + original: Union[str, None] = None, + ): + """ + Request content from the assistant. + """ + + generation_context = ContentGenerationContext( + context=context, + instructions=instructions, + length=length, + character=character, + original=original, + ) + + return await self.contextual_generate(generation_context) + + @set_processing + async def contextual_generate( + self, + generation_context: ContentGenerationContext, + ): + """ + Request content from the assistant. + """ + + context_typ, context_name = generation_context.computed_context + + if generation_context.length < 100: + kind = "create_short" + elif generation_context.length < 500: + kind = "create_concise" + else: + kind = "create" + + content = await Prompt.request( + f"creator.contextual-generate", + self.client, + kind, + vars={ + "scene": self.scene, + "max_tokens": self.client.max_token_length, + "generation_context": generation_context, + "context_typ": context_typ, + "context_name": context_name, + "character": ( + self.scene.get_character(generation_context.character) + if generation_context.character + else None + ), + }, + ) + + content = util.strip_partial_sentences(content) + + return content.strip() diff --git a/src/talemate/agents/creator/character.py b/src/talemate/agents/creator/character.py index 8b01a603..a86f1d08 100644 --- a/src/talemate/agents/creator/character.py +++ b/src/talemate/agents/creator/character.py @@ -208,6 +208,25 @@ class CharacterCreatorMixin: ) return attributes + @set_processing + async def determine_character_name( + self, + character_name: str, + allowed_names: list[str] = None, + ) -> str: + name = await Prompt.request( + f"creator.determine-character-name", + self.client, + "analyze_freeform_short", + vars={ + "scene": self.scene, + "max_tokens": self.client.max_token_length, + "character_name": character_name, + "allowed_names": allowed_names or [], + }, + ) + return name.split('"', 1)[0].strip().strip(".").strip() + @set_processing async def determine_character_description( self, character: Character, text: str = "" diff --git a/src/talemate/agents/creator/scenario.py b/src/talemate/agents/creator/scenario.py index a232ed60..3cdd3672 100644 --- a/src/talemate/agents/creator/scenario.py +++ b/src/talemate/agents/creator/scenario.py @@ -7,7 +7,6 @@ from talemate.prompts import Prompt class ScenarioCreatorMixin: - """ Adds scenario creation functionality to the creator agent """ diff --git a/src/talemate/agents/custom/__init__.py b/src/talemate/agents/custom/__init__.py new file mode 100644 index 00000000..7b4510b3 --- /dev/null +++ b/src/talemate/agents/custom/__init__.py @@ -0,0 +1,34 @@ +import importlib +import os + +import structlog + +log = structlog.get_logger("talemate.agents.custom") + +# import every submodule in this directory +# +# each directory in this directory is a submodule + +# get the current directory +current_directory = os.path.dirname(__file__) + +# get all subdirectories +subdirectories = [ + os.path.join(current_directory, name) + for name in os.listdir(current_directory) + if os.path.isdir(os.path.join(current_directory, name)) +] + +# import every submodule + +for subdirectory in subdirectories: + # get the name of the submodule + submodule_name = os.path.basename(subdirectory) + + if submodule_name.startswith("__"): + continue + + log.info("activating custom agent", module=submodule_name) + + # import the submodule + importlib.import_module(f".{submodule_name}", __package__) diff --git a/src/talemate/agents/custom/third_party_agents_go_here.txt b/src/talemate/agents/custom/third_party_agents_go_here.txt new file mode 100644 index 00000000..3d0aee4d --- /dev/null +++ b/src/talemate/agents/custom/third_party_agents_go_here.txt @@ -0,0 +1,5 @@ +Each agent should be in its own subdirectory. + +The subdirectory itself must be a valid python module. + +Check out docs/dev/agents/example/test for a very simplistic custom agent example. \ No newline at end of file diff --git a/src/talemate/agents/director.py b/src/talemate/agents/director.py index 0d8ee662..f0374045 100644 --- a/src/talemate/agents/director.py +++ b/src/talemate/agents/director.py @@ -182,7 +182,10 @@ class DirectorAgent(Agent): # no character, see if there are NPC characters at all # if not we always want to direct narration - always_direct = not self.scene.npc_character_names + always_direct = ( + not self.scene.npc_character_names + or self.scene.game_state.ops.always_direct + ) next_direct = self.next_direct_scene @@ -253,6 +256,34 @@ class DirectorAgent(Agent): # run scene instructions self.scene.game_state.scene_instructions + @set_processing + async def persist_characters_from_worldstate( + self, exclude: list[str] = None + ) -> List[Character]: + log.warning( + "persist_characters_from_worldstate", + world_state_characters=self.scene.world_state.characters, + scene_characters=self.scene.character_names, + ) + + created_characters = [] + + for character_name in self.scene.world_state.characters.keys(): + + if exclude and character_name.lower() in exclude: + continue + + if character_name in self.scene.character_names: + continue + + character = await self.persist_character(name=character_name) + + created_characters.append(character) + + self.scene.emit_status() + + return created_characters + @set_processing async def persist_character( self, @@ -262,7 +293,10 @@ class DirectorAgent(Agent): ): world_state = instance.get_agent("world_state") creator = instance.get_agent("creator") + self.scene.log.debug("persist_character", name=name) + name = await creator.determine_character_name(name) + self.scene.log.debug("persist_character", adjusted_name=name) character = self.scene.Character(name=name) character.color = random.choice( diff --git a/src/talemate/agents/editor.py b/src/talemate/agents/editor.py index 91b5ad90..72eca61c 100644 --- a/src/talemate/agents/editor.py +++ b/src/talemate/agents/editor.py @@ -40,11 +40,6 @@ class EditorAgent(Agent): self.client = client self.is_enabled = True self.actions = { - "edit_dialogue": AgentAction( - enabled=False, - label="Edit dialogue", - description="Will attempt to improve the quality of dialogue based on the character and scene. Runs automatically after each AI dialogue.", - ), "fix_exposition": AgentAction( enabled=True, label="Fix exposition", @@ -100,8 +95,6 @@ class EditorAgent(Agent): for text in emission.generation: edit = await self.add_detail(text, emission.character) - edit = await self.edit_conversation(edit, emission.character) - edit = await self.fix_exposition(edit, emission.character) edited.append(edit) @@ -126,35 +119,6 @@ class EditorAgent(Agent): emission.generation = edited - @set_processing - async def edit_conversation(self, content: str, character: Character): - """ - Edits a conversation - """ - - if not self.actions["edit_dialogue"].enabled: - return content - - response = await Prompt.request( - "editor.edit-dialogue", - self.client, - "edit_dialogue", - vars={ - "content": content, - "character": character, - "scene": self.scene, - "max_length": self.client.max_token_length, - }, - ) - - response = response.split("[end]")[0] - - response = util.replace_exposition_markers(response) - response = util.clean_dialogue(response, main_name=character.name) - response = util.strip_partial_sentences(response) - - return response - @set_processing async def fix_exposition(self, content: str, character: Character): """ @@ -169,7 +133,7 @@ class EditorAgent(Agent): content = util.strip_partial_sentences(content) character_prefix = f"{character.name}: " message = content.split(character_prefix)[1] - content = f"{character_prefix}*{message.strip('*')}*" + content = f'{character_prefix}"{message.strip()}"' return content elif '"' in content: # silly hack to clean up some LLMs that always start with a quote diff --git a/src/talemate/agents/memory.py b/src/talemate/agents/memory.py index 6625d165..f80d3ad2 100644 --- a/src/talemate/agents/memory.py +++ b/src/talemate/agents/memory.py @@ -425,7 +425,13 @@ class ChromaDBMemoryAgent(MemoryAgent): def make_collection_name(self, scene): if self.USE_OPENAI: - suffix = "-openai" + model_name = self.config.get("chromadb").get( + "openai_model", "text-embedding-3-small" + ) + if model_name == "text-embedding-ada-002": + suffix = "-openai" + else: + suffix = f"-openai-{model_name}" elif self.USE_INSTRUCTOR: suffix = "-instructor" model = self.config.get("chromadb").get( @@ -472,12 +478,19 @@ class ChromaDBMemoryAgent(MemoryAgent): "You must provide an the openai ai key in the config if you want to use it for chromadb embeddings" ) + model_name = self.config.get("chromadb").get( + "openai_model", "text-embedding-3-small" + ) + log.info( - "crhomadb", status="using openai", openai_key=openai_key[:5] + "..." + "crhomadb", + status="using openai", + openai_key=openai_key[:5] + "...", + model=model_name, ) openai_ef = embedding_functions.OpenAIEmbeddingFunction( api_key=openai_key, - model_name="text-embedding-ada-002", + model_name=model_name, ) self.db = self.db_client.get_or_create_collection( collection_name, embedding_function=openai_ef diff --git a/src/talemate/agents/narrator.py b/src/talemate/agents/narrator.py index 54bb0a8a..af29a4bc 100644 --- a/src/talemate/agents/narrator.py +++ b/src/talemate/agents/narrator.py @@ -2,6 +2,7 @@ from __future__ import annotations import dataclasses import random +from functools import wraps from typing import TYPE_CHECKING, Callable, List, Optional, Union import structlog @@ -40,7 +41,8 @@ def set_processing(fn): """ @_set_processing - async def wrapper(self, *args, **kwargs): + @wraps(fn) + async def narration_wrapper(self, *args, **kwargs): response = await fn(self, *args, **kwargs) emission = NarratorAgentEmission( agent=self, @@ -49,13 +51,11 @@ def set_processing(fn): await talemate.emit.async_signals.get("agent.narrator.generated").send(emission) return emission.generation[0] - wrapper.__name__ = fn.__name__ - return wrapper + return narration_wrapper @register() class NarratorAgent(Agent): - """ Handles narration of the story """ @@ -524,9 +524,34 @@ class NarratorAgent(Agent): return response + @set_processing + async def paraphrase(self, narration: str): + """ + Paraphrase a narration + """ + + response = await Prompt.request( + "narrator.paraphrase", + self.client, + "narrate", + vars={ + "text": narration, + "scene": self.scene, + "max_tokens": self.client.max_token_length, + }, + ) + + log.info("paraphrase", narration=narration, response=response) + + response = self.clean_result(response.strip().strip("*")) + response = f"*{response}*" + + return response + async def action_to_narration( self, action_name: str, + emit_message: bool = False, *args, **kwargs, ): @@ -539,6 +564,10 @@ class NarratorAgent(Agent): narration, source=f"{action_name}:{args[0] if args else ''}".rstrip(":") ) self.scene.push_history(narrator_message) + + if emit_message: + emit("narrator", narrator_message) + return narrator_message # LLM client related methods. These are called during or after the client diff --git a/src/talemate/agents/summarize.py b/src/talemate/agents/summarize.py index 3d263619..24b15650 100644 --- a/src/talemate/agents/summarize.py +++ b/src/talemate/agents/summarize.py @@ -262,9 +262,11 @@ class SummarizeAgent(Agent): "dialogue": text, "scene": self.scene, "max_tokens": self.client.max_token_length, - "summarization_method": self.actions["archive"].config["method"].value - if method is None - else method, + "summarization_method": ( + self.actions["archive"].config["method"].value + if method is None + else method + ), "extra_context": extra_context or "", "extra_instructions": extra_instructions or "", }, diff --git a/src/talemate/agents/tts.py b/src/talemate/agents/tts.py index cfb5ec0d..2b40a84f 100644 --- a/src/talemate/agents/tts.py +++ b/src/talemate/agents/tts.py @@ -109,7 +109,6 @@ class VoiceLibrary(pydantic.BaseModel): @register() class TTSAgent(Agent): - """ Text to speech agent """ @@ -135,7 +134,6 @@ class TTSAgent(Agent): self.voices = { "elevenlabs": VoiceLibrary(api="elevenlabs"), - "coqui": VoiceLibrary(api="coqui"), "tts": VoiceLibrary(api="tts"), } self.config = config.load_config() @@ -149,10 +147,8 @@ class TTSAgent(Agent): "api": AgentActionConfig( type="text", choices=[ - # TODO at local TTS support {"value": "tts", "label": "TTS (Local)"}, {"value": "elevenlabs", "label": "Eleven Labs"}, - {"value": "coqui", "label": "Coqui Studio"}, ], value="tts", label="API", @@ -551,97 +547,3 @@ class TTSAgent(Agent): voices.sort(key=lambda x: x.label) return voices - - # COQUI STUDIO - - async def _generate_coqui(self, text: str) -> Union[bytes, None]: - api_key = self.token - if not api_key: - return - - async with httpx.AsyncClient() as client: - url = "https://app.coqui.ai/api/v2/samples/xtts/render/" - headers = { - "Accept": "application/json", - "Content-Type": "application/json", - "Authorization": f"Bearer {api_key}", - } - data = { - "voice_id": self.default_voice_id, - "text": text, - "language": "en", # Assuming English language for simplicity; this could be parameterized - } - - # Make the POST request to Coqui API - response = await client.post(url, json=data, headers=headers, timeout=300) - if response.status_code in [200, 201]: - # Parse the JSON response to get the audio URL - response_data = response.json() - audio_url = response_data.get("audio_url") - if audio_url: - # Make a GET request to download the audio file - audio_response = await client.get(audio_url) - if audio_response.status_code == 200: - # delete the sample from Coqui Studio - # await self._cleanup_coqui(response_data.get('id')) - return audio_response.content - else: - log.error(f"Error downloading audio: {audio_response.text}") - else: - log.error("No audio URL in response") - else: - log.error(f"Error generating audio: {response.text}") - - async def _cleanup_coqui(self, sample_id: str): - api_key = self.token - if not api_key or not sample_id: - return - - async with httpx.AsyncClient() as client: - url = f"https://app.coqui.ai/api/v2/samples/xtts/{sample_id}" - headers = {"Authorization": f"Bearer {api_key}"} - - # Make the DELETE request to Coqui API - response = await client.delete(url, headers=headers) - - if response.status_code == 204: - log.info(f"Successfully deleted sample with ID: {sample_id}") - else: - log.error( - f"Error deleting sample with ID: {sample_id}: {response.text}" - ) - - async def _list_voices_coqui(self) -> dict[str, str]: - url_speakers = "https://app.coqui.ai/api/v2/speakers" - url_custom_voices = "https://app.coqui.ai/api/v2/voices" - - voices = [] - - async with httpx.AsyncClient() as client: - headers = {"Authorization": f"Bearer {self.token}"} - response = await client.get( - url_speakers, headers=headers, params={"per_page": 1000} - ) - speakers = response.json()["result"] - voices.extend( - [ - Voice(value=speaker["id"], label=speaker["name"]) - for speaker in speakers - ] - ) - - response = await client.get( - url_custom_voices, headers=headers, params={"per_page": 1000} - ) - custom_voices = response.json()["result"] - voices.extend( - [ - Voice(value=voice["id"], label=voice["name"]) - for voice in custom_voices - ] - ) - - # sort by name - voices.sort(key=lambda x: x.label) - - return voices diff --git a/src/talemate/agents/world_state.py b/src/talemate/agents/world_state.py index 2a112ab4..8f72b8c9 100644 --- a/src/talemate/agents/world_state.py +++ b/src/talemate/agents/world_state.py @@ -187,7 +187,7 @@ class WorldStateAgent(Agent): await self.check_pin_conditions() - async def update_world_state(self): + async def update_world_state(self, force: bool = False): if not self.enabled: return @@ -206,7 +206,7 @@ class WorldStateAgent(Agent): self.next_update % self.actions["update_world_state"].config["turns"].value != 0 or self.next_update == 0 - ): + ) and not force: self.next_update += 1 return @@ -349,11 +349,15 @@ class WorldStateAgent(Agent): self, text: str, instruction: str, + short: bool = False, ): + + kind = "analyze_freeform_short" if short else "analyze_freeform" + response = await Prompt.request( "world_state.analyze-text-and-follow-instruction", self.client, - "analyze_freeform", + kind, vars={ "scene": self.scene, "max_tokens": self.client.max_token_length, @@ -376,11 +380,13 @@ class WorldStateAgent(Agent): self, text: str, query: str, + short: bool = False, ): + kind = "analyze_freeform_short" if short else "analyze_freeform" response = await Prompt.request( "world_state.analyze-text-and-answer-question", self.client, - "analyze_freeform", + kind, vars={ "scene": self.scene, "max_tokens": self.client.max_token_length, @@ -439,6 +445,7 @@ class WorldStateAgent(Agent): self, name: str, text: str = None, + alteration_instructions: str = None, ): """ Attempts to extract a character sheet from the given text. @@ -453,6 +460,8 @@ class WorldStateAgent(Agent): "max_tokens": self.client.max_token_length, "text": text, "name": name, + "character": self.scene.get_character(name), + "alteration_instructions": alteration_instructions or "", }, ) @@ -527,9 +536,11 @@ class WorldStateAgent(Agent): "max_tokens": self.client.max_token_length, "question": reinforcement.question, "instructions": reinforcement.instructions or "", - "character": self.scene.get_character(reinforcement.character) - if reinforcement.character - else None, + "character": ( + self.scene.get_character(reinforcement.character) + if reinforcement.character + else None + ), "answer": (reinforcement.answer if not reset else None) or "", "reinforcement": reinforcement, }, @@ -735,3 +746,28 @@ class WorldStateAgent(Agent): ) return is_leaving.lower().startswith("y") + + @set_processing + async def manager(self, action_name: str, *args, **kwargs): + """ + Executes a world state manager action through self.scene.world_state_manager + """ + + manager = self.scene.world_state_manager + + try: + fn = getattr(manager, action_name, None) + + if not fn: + raise ValueError(f"Unknown action: {action_name}") + + return await fn(*args, **kwargs) + except Exception as e: + log.error( + "worldstate.manager", + action_name=action_name, + args=args, + kwargs=kwargs, + error=e, + ) + raise diff --git a/src/talemate/client/base.py b/src/talemate/client/base.py index 0b1660a0..911ac300 100644 --- a/src/talemate/client/base.py +++ b/src/talemate/client/base.py @@ -1,6 +1,7 @@ """ A unified client base, based on the openai API """ + import logging import random import time @@ -32,6 +33,19 @@ REMOTE_SERVICES = [ STOPPING_STRINGS = ["<|im_end|>", ""] +class PromptData(pydantic.BaseModel): + kind: str + prompt: str + response: str + prompt_tokens: int + response_tokens: int + client_name: str + client_type: str + time: Union[float, int] + agent_stack: list[str] = pydantic.Field(default_factory=list) + generation_parameters: dict = pydantic.Field(default_factory=dict) + + class ErrorAction(pydantic.BaseModel): title: str action_name: str @@ -154,7 +168,7 @@ class ClientBase: """ if self.decensor_enabled: - + if "narrate" in kind: return system_prompts.NARRATOR if "story" in kind: @@ -179,9 +193,9 @@ class ClientBase: return system_prompts.ANALYST if "summarize" in kind: return system_prompts.SUMMARIZE - + else: - + if "narrate" in kind: return system_prompts.NARRATOR_NO_DECENSOR if "story" in kind: @@ -206,7 +220,7 @@ class ClientBase: return system_prompts.ANALYST_NO_DECENSOR if "summarize" in kind: return system_prompts.SUMMARIZE_NO_DECENSOR - + return system_prompts.BASIC def emit_status(self, processing: bool = None): @@ -411,16 +425,22 @@ class ClientBase: response = response.split(stopping_string)[0] break + agent_context = active_agent.get() + emit( "prompt_sent", - data={ - "kind": kind, - "prompt": finalized_prompt, - "response": response, - "prompt_tokens": token_length, - "response_tokens": self.count_tokens(response), - "time": time_end - time_start, - }, + data=PromptData( + kind=kind, + prompt=finalized_prompt, + response=response, + prompt_tokens=token_length, + response_tokens=self.count_tokens(response), + agent_stack=agent_context.agent_stack if agent_context else [], + client_name=self.name, + client_type=self.client_type, + time=time_end - time_start, + generation_parameters=prompt_param, + ).model_dump(), ) return response diff --git a/src/talemate/client/custom/__init__.py b/src/talemate/client/custom/__init__.py new file mode 100644 index 00000000..0997309a --- /dev/null +++ b/src/talemate/client/custom/__init__.py @@ -0,0 +1,34 @@ +import importlib +import os + +import structlog + +log = structlog.get_logger("talemate.client.custom") + +# import every submodule in this directory +# +# each directory in this directory is a submodule + +# get the current directory +current_directory = os.path.dirname(__file__) + +# get all subdirectories +subdirectories = [ + os.path.join(current_directory, name) + for name in os.listdir(current_directory) + if os.path.isdir(os.path.join(current_directory, name)) +] + +# import every submodule + +for subdirectory in subdirectories: + # get the name of the submodule + submodule_name = os.path.basename(subdirectory) + + if submodule_name.startswith("__"): + continue + + log.info("activating custom client", module=submodule_name) + + # import the submodule + importlib.import_module(f".{submodule_name}", __package__) diff --git a/src/talemate/client/custom/third_party_clients_go_here.txt b/src/talemate/client/custom/third_party_clients_go_here.txt new file mode 100644 index 00000000..dfab27b5 --- /dev/null +++ b/src/talemate/client/custom/third_party_clients_go_here.txt @@ -0,0 +1,5 @@ +Each client should be in its own subdirectory. + +The subdirectory itself must be a valid python module. + +Check out docs/dev/client/example/test for a very simplistic custom client example. \ No newline at end of file diff --git a/src/talemate/client/lmstudio.py b/src/talemate/client/lmstudio.py index 0282d349..0fa160db 100644 --- a/src/talemate/client/lmstudio.py +++ b/src/talemate/client/lmstudio.py @@ -9,6 +9,7 @@ class Defaults(pydantic.BaseModel): api_url: str = "http://localhost:1234" max_token_length: int = 4096 + @register() class LMStudioClient(ClientBase): client_type = "lmstudio" diff --git a/src/talemate/client/model_prompts.py b/src/talemate/client/model_prompts.py index 42c81489..38b4fbe1 100644 --- a/src/talemate/client/model_prompts.py +++ b/src/talemate/client/model_prompts.py @@ -38,7 +38,6 @@ log = structlog.get_logger("talemate.model_prompts") class ModelPrompt: - """ Will attempt to load an LLM prompt template based on the model name diff --git a/src/talemate/client/openai.py b/src/talemate/client/openai.py index 11588080..71505e62 100644 --- a/src/talemate/client/openai.py +++ b/src/talemate/client/openai.py @@ -16,6 +16,25 @@ __all__ = [ ] log = structlog.get_logger("talemate") +# Edit this to add new models / remove old models +SUPPORTED_MODELS = [ + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo", + "gpt-4", + "gpt-4-1106-preview", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", +] + +JSON_OBJECT_RESPONSE_MODELS = [ + "gpt-4-1106-preview", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-3.5-turbo-0125", +] + def num_tokens_from_messages(messages: list[dict], model: str = "gpt-3.5-turbo-0613"): """Return the number of tokens used by a list of messages.""" @@ -90,14 +109,7 @@ class OpenAIClient(ClientBase): name_prefix: str = "OpenAI" title: str = "OpenAI" manual_model: bool = True - manual_model_choices: list[str] = [ - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-4", - "gpt-4-1106-preview", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - ] + manual_model_choices: list[str] = SUPPORTED_MODELS requires_prompt_template: bool = False defaults: Defaults = Defaults() @@ -216,7 +228,7 @@ class OpenAIClient(ClientBase): if "<|BOT|>" in prompt: _, right = prompt.split("<|BOT|>", 1) if right: - prompt = prompt.replace("<|BOT|>", "\nContinue this response: ") + prompt = prompt.replace("<|BOT|>", "\nStart your response with: ") else: prompt = prompt.replace("<|BOT|>", "") @@ -229,6 +241,15 @@ class OpenAIClient(ClientBase): valid_keys = ["temperature", "top_p"] + # GPT-3.5 models tend to run away with the generated + # response size so we allow talemate to set the max_tokens + # + # GPT-4 on the other hand seems to benefit from letting it + # decide the generation length naturally and it will generally + # produce reasonably sized responses + if self.model_name.startswith("gpt-3.5-"): + valid_keys.append("max_tokens") + for key in keys: if key not in valid_keys: del parameters[key] @@ -242,10 +263,14 @@ class OpenAIClient(ClientBase): raise Exception("No OpenAI API key set") # only gpt-4-* supports enforcing json object - supports_json_object = self.model_name.startswith("gpt-4-") + supports_json_object = ( + self.model_name.startswith("gpt-4-") + or self.model_name in JSON_OBJECT_RESPONSE_MODELS + ) right = None + expected_response = None try: - _, right = prompt.split("\nContinue this response: ") + _, right = prompt.split("\nStart your response with: ") expected_response = right.strip() if expected_response.startswith("{") and supports_json_object: parameters["response_format"] = {"type": "json_object"} @@ -255,8 +280,13 @@ class OpenAIClient(ClientBase): human_message = {"role": "user", "content": prompt.strip()} system_message = {"role": "system", "content": self.get_system_message(kind)} - self.log.debug("generate", prompt=prompt[:128] + " ...", parameters=parameters, system_message=system_message) - + self.log.debug( + "generate", + prompt=prompt[:128] + " ...", + parameters=parameters, + system_message=system_message, + ) + try: response = await self.client.chat.completions.create( model=self.model_name, @@ -266,6 +296,17 @@ class OpenAIClient(ClientBase): response = response.choices[0].message.content + # older models don't support json_object response coersion + # and often like to return the response wrapped in ```json + # so we strip that out if the expected response is a json object + if ( + not supports_json_object + and expected_response + and expected_response.startswith("{") + ): + if response.startswith("```json") and response.endswith("```"): + response = response[7:-3].strip() + if right and response.startswith(right): response = response[len(right) :].strip() diff --git a/src/talemate/client/system_prompts.py b/src/talemate/client/system_prompts.py index 8d10493a..9d4ed90b 100644 --- a/src/talemate/client/system_prompts.py +++ b/src/talemate/client/system_prompts.py @@ -32,7 +32,9 @@ DIRECTOR_NO_DECENSOR = str(Prompt.get("director.system-no-decensor")) ANALYST_NO_DECENSOR = str(Prompt.get("world_state.system-analyst-no-decensor")) -ANALYST_FREEFORM_NO_DECENSOR = str(Prompt.get("world_state.system-analyst-freeform-no-decensor")) +ANALYST_FREEFORM_NO_DECENSOR = str( + Prompt.get("world_state.system-analyst-freeform-no-decensor") +) EDITOR_NO_DECENSOR = str(Prompt.get("editor.system-no-decensor")) diff --git a/src/talemate/commands/cmd_characters.py b/src/talemate/commands/cmd_characters.py index 2b8c43b2..e711182b 100644 --- a/src/talemate/commands/cmd_characters.py +++ b/src/talemate/commands/cmd_characters.py @@ -79,7 +79,7 @@ class CmdDeactivateCharacter(TalemateCommand): ) message = await narrator.action_to_narration( "narrate_character_exit", - self.scene.get_character(character_name), + character=self.scene.get_character(character_name), direction=direction, ) self.narrator_message(message) @@ -159,7 +159,7 @@ class CmdActivateCharacter(TalemateCommand): ) message = await narrator.action_to_narration( "narrate_character_entry", - self.scene.get_character(character_name), + character=self.scene.get_character(character_name), direction=direction, ) self.narrator_message(message) diff --git a/src/talemate/commands/cmd_debug_tools.py b/src/talemate/commands/cmd_debug_tools.py index 07dd6d48..6722667d 100644 --- a/src/talemate/commands/cmd_debug_tools.py +++ b/src/talemate/commands/cmd_debug_tools.py @@ -1,6 +1,9 @@ import asyncio +import json import logging +import structlog + from talemate.commands.base import TalemateCommand from talemate.commands.manager import register from talemate.prompts.base import set_default_sectioning_handler @@ -12,6 +15,8 @@ __all__ = [ "CmdRunAutomatic", ] +log = structlog.get_logger("talemate.commands.cmd_debug_tools") + @register class CmdDebugOn(TalemateCommand): @@ -144,3 +149,32 @@ class CmdSetContentContext(TalemateCommand): self.scene.context = context self.emit("system", f"Content context set to {context}") + + +@register +class CmdDumpHistory(TalemateCommand): + """ + Command class for the 'dump_history' command + """ + + name = "dump_history" + description = "Dump the history of the scene" + aliases = [] + + async def run(self): + for entry in self.scene.history: + log.debug("dump_history", entry=entry) + + +@register +class CmdDumpSceneSerialization(TalemateCommand): + """ + Command class for the 'dump_scene_serialization' command + """ + + name = "dump_scene_serialization" + description = "Dump the scene serialization" + aliases = [] + + async def run(self): + log.debug("dump_scene_serialization", serialization=self.scene.json) diff --git a/src/talemate/commands/cmd_dialogue.py b/src/talemate/commands/cmd_dialogue.py index 8ae44c13..4e0b9991 100644 --- a/src/talemate/commands/cmd_dialogue.py +++ b/src/talemate/commands/cmd_dialogue.py @@ -36,7 +36,11 @@ class CmdAIDialogue(TalemateCommand): if conversation_agent.actions["natural_flow"].enabled: await conversation_agent.apply_natural_flow(force=True, npcs_only=True) character_name = self.scene.next_actor - actor = self.scene.get_character(character_name).actor + try: + actor = self.scene.get_character(character_name).actor + except AttributeError: + return + if actor.character.is_player: actor = random.choice(list(self.scene.get_npc_characters())).actor else: diff --git a/src/talemate/commands/cmd_rebuild_archive.py b/src/talemate/commands/cmd_rebuild_archive.py index 5bc2ca2e..74c2b957 100644 --- a/src/talemate/commands/cmd_rebuild_archive.py +++ b/src/talemate/commands/cmd_rebuild_archive.py @@ -26,6 +26,12 @@ class CmdRebuildArchive(TalemateCommand): ah for ah in self.scene.archived_history if ah.get("end") is None ] + self.scene.ts = ( + self.scene.archived_history[-1].ts + if self.scene.archived_history + else "PT0S" + ) + while True: more = await summarizer.agent.build_archive(self.scene) diff --git a/src/talemate/commands/cmd_world_state.py b/src/talemate/commands/cmd_world_state.py index a6d6a957..308282c8 100644 --- a/src/talemate/commands/cmd_world_state.py +++ b/src/talemate/commands/cmd_world_state.py @@ -50,7 +50,6 @@ class CmdWorldState(TalemateCommand): @register class CmdPersistCharacter(TalemateCommand): - """ Will attempt to create an actual character from a currently non tracked character in the scene, by name. @@ -177,7 +176,6 @@ class CmdPersistCharacter(TalemateCommand): @register class CmdAddReinforcement(TalemateCommand): - """ Will attempt to create an actual character from a currently non tracked character in the scene, by name. @@ -204,7 +202,6 @@ class CmdAddReinforcement(TalemateCommand): @register class CmdRemoveReinforcement(TalemateCommand): - """ Will attempt to create an actual character from a currently non tracked character in the scene, by name. @@ -236,7 +233,6 @@ class CmdRemoveReinforcement(TalemateCommand): @register class CmdUpdateReinforcements(TalemateCommand): - """ Will attempt to create an actual character from a currently non tracked character in the scene, by name. @@ -258,7 +254,6 @@ class CmdUpdateReinforcements(TalemateCommand): @register class CmdCheckPinConditions(TalemateCommand): - """ Will attempt to create an actual character from a currently non tracked character in the scene, by name. @@ -277,7 +272,6 @@ class CmdCheckPinConditions(TalemateCommand): @register class CmdApplyWorldStateTemplate(TalemateCommand): - """ Will apply a world state template setting up automatic state tracking. @@ -337,7 +331,6 @@ class CmdApplyWorldStateTemplate(TalemateCommand): @register class CmdSummarizeAndPin(TalemateCommand): - """ Will take a message index and then walk back N messages summarizing the scene and pinning it to the context. diff --git a/src/talemate/config.py b/src/talemate/config.py index f6259f1e..37662dee 100644 --- a/src/talemate/config.py +++ b/src/talemate/config.py @@ -7,6 +7,8 @@ import structlog import yaml from pydantic import BaseModel, Field +from talemate.agents.registry import get_agent_class +from talemate.client.registry import get_client_class from talemate.emit import emit from talemate.scene_assets import Asset @@ -16,6 +18,16 @@ if TYPE_CHECKING: log = structlog.get_logger("talemate.config") +def scenes_dir(): + relative_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "..", + "scenes", + ) + return os.path.abspath(relative_path) + + class Client(BaseModel): type: str name: str @@ -87,6 +99,9 @@ class WorldStateTemplates(BaseModel): default_factory=dict ) + def get_template(self, name: str) -> Union[StateReinforcementTemplate, None]: + return self.state_reinforcement.get(name) + class WorldState(BaseModel): templates: WorldStateTemplates = WorldStateTemplates() @@ -138,6 +153,7 @@ class TTSConfig(BaseModel): class ChromaDB(BaseModel): instructor_device: str = "cpu" instructor_model: str = "default" + openai_model: str = "text-embedding-3-small" embeddings: str = "default" @@ -149,8 +165,56 @@ class RecentScene(BaseModel): cover_image: Union[Asset, None] = None +def gnerate_intro_scenes(): + """ + When there are no recent scenes, generate from a set of introdutory scenes + """ + + scenes = [ + RecentScene( + name="Simulation Suite", + path=os.path.join( + scenes_dir(), "simulation-suite", "simulation-suite.json" + ), + filename="simulation-suite.json", + date=datetime.datetime.now().isoformat(), + cover_image=Asset( + id="4b157dccac2ba71adb078a9d591f9900d6d62f3e86168a5e0e5e1e9faf6dc103", + file_type="png", + media_type="image/png", + ), + ), + RecentScene( + name="Infinity Quest", + path=os.path.join(scenes_dir(), "infinity-quest", "infinity-quest.json"), + filename="infinity-quest.json", + date=datetime.datetime.now().isoformat(), + cover_image=Asset( + id="52b1388ed6f77a43981bd27e05df54f16e12ba8de1c48f4b9bbcb138fa7367df", + file_type="png", + media_type="image/png", + ), + ), + RecentScene( + name="Infinity Quest Dynamic Scenario", + path=os.path.join( + scenes_dir(), "infinity-quest-dynamic-scenario", "infinity-quest.json" + ), + filename="infinity-quest.json", + date=datetime.datetime.now().isoformat(), + cover_image=Asset( + id="e7c712a0b276342d5767ba23806b03912d10c7c4b82dd1eec0056611e2cd5404", + file_type="png", + media_type="image/png", + ), + ), + ] + + return scenes + + class RecentScenes(BaseModel): - scenes: list[RecentScene] = pydantic.Field(default_factory=list) + scenes: list[RecentScene] = pydantic.Field(default_factory=gnerate_intro_scenes) max_entries: int = 10 def push(self, scene: "Scene"): @@ -175,9 +239,11 @@ class RecentScenes(BaseModel): path=scene.full_path, filename=scene.filename, date=now.isoformat(), - cover_image=scene.assets.assets[scene.assets.cover_image] - if scene.assets.cover_image - else None, + cover_image=( + scene.assets.assets[scene.assets.cover_image] + if scene.assets.cover_image + else None + ), ), ) @@ -279,3 +345,44 @@ def save_config(config, file_path: str = "./config.yaml"): yaml.dump(config, file) emit("config_saved", data=config) + + +def cleanup(): + + log.info("cleaning up config") + + config = load_config(as_model=True) + + cleanup_removed_clients(config) + cleanup_removed_agents(config) + + save_config(config) + + +def cleanup_removed_clients(config: Config): + """ + Will remove any clients that are no longer present + """ + + if not config: + return + + for client_in_config in list(config.clients.keys()): + client_config = config.clients[client_in_config] + if not get_client_class(client_config.type): + log.info("removing client from config", client=client_in_config) + del config.clients[client_in_config] + + +def cleanup_removed_agents(config: Config): + """ + Will remove any agents that are no longer present + """ + + if not config: + return + + for agent_in_config in list(config.agents.keys()): + if not get_agent_class(agent_in_config): + log.info("removing agent from config", agent=agent_in_config) + del config.agents[agent_in_config] diff --git a/src/talemate/game_state.py b/src/talemate/game_state.py index 94aa1a27..3278af8a 100644 --- a/src/talemate/game_state.py +++ b/src/talemate/game_state.py @@ -29,6 +29,7 @@ class Instructions(pydantic.BaseModel): class Ops(pydantic.BaseModel): run_on_start: bool = False + always_direct: bool = False class GameState(pydantic.BaseModel): @@ -95,8 +96,8 @@ class GameState(pydantic.BaseModel): def has_var(self, key: str) -> bool: return key in self.variables - def get_var(self, key: str) -> Any: - return self.variables[key] + def get_var(self, key: str, default: Any = None) -> Any: + return self.variables.get(key, default) def get_or_set_var(self, key: str, value: Any, commit: bool = False) -> Any: if not self.has_var(key): diff --git a/src/talemate/instance.py b/src/talemate/instance.py index 8b5a4f6b..07083a78 100644 --- a/src/talemate/instance.py +++ b/src/talemate/instance.py @@ -1,6 +1,7 @@ """ Keep track of clients and agents """ + import asyncio import structlog diff --git a/src/talemate/load.py b/src/talemate/load.py index c84522d3..6e3e8dd3 100644 --- a/src/talemate/load.py +++ b/src/talemate/load.py @@ -174,6 +174,9 @@ async def load_scene_from_data( scene.filename = None scene.goals = scene_data.get("goals", []) scene.immutable_save = scene_data.get("immutable_save", False) + scene.experimental = scene_data.get("experimental", False) + scene.help = scene_data.get("help", "") + scene.restore_from = scene_data.get("restore_from", "") # reset = True @@ -240,9 +243,13 @@ async def load_scene_from_data( actor = Actor(character, agent) else: actor = Player(character, None) - # Add the TestCharacter actor to the scene await scene.add_actor(actor) + # if there is nio player character, add the default player character + + if not scene.get_player_character(): + await scene.add_actor(default_player_character()) + # the scene has been saved before (since we just loaded it), so we set the saved flag to True # as long as the scene has a memory_id. scene.saved = "memory_id" in scene_data diff --git a/src/talemate/prompts/base.py b/src/talemate/prompts/base.py index 744ef8fd..bdfd9565 100644 --- a/src/talemate/prompts/base.py +++ b/src/talemate/prompts/base.py @@ -205,9 +205,14 @@ class LoopedPrompt: self._current_item = None +class JoinableList(list): + + def join(self, separator: str = "\n"): + return separator.join(self) + + @dataclasses.dataclass class Prompt: - """ Base prompt class. """ @@ -355,6 +360,7 @@ class Prompt: env.globals["query_scene"] = self.query_scene env.globals["query_memory"] = self.query_memory env.globals["query_text"] = self.query_text + env.globals["query_text_eval"] = self.query_text_eval env.globals["instruct_text"] = self.instruct_text env.globals["agent_action"] = self.agent_action env.globals["retrieve_memories"] = self.retrieve_memories @@ -364,6 +370,8 @@ class Prompt: env.globals["len"] = lambda x: len(x) env.globals["max"] = lambda x, y: max(x, y) env.globals["min"] = lambda x, y: min(x, y) + env.globals["make_list"] = lambda: JoinableList() + env.globals["make_dict"] = lambda: {} env.globals["count_tokens"] = lambda x: count_tokens( dedupe_string(x, debug=False) ) @@ -372,6 +380,7 @@ class Prompt: env.globals["emit_system"] = lambda status, message: emit( "system", status=status, message=message ) + env.globals["emit_narrator"] = lambda message: emit("system", message=message) env.filters["condensed"] = condensed ctx.update(self.vars) @@ -439,10 +448,14 @@ class Prompt: vars.update(kwargs) return Prompt.get(uid, vars=vars) - def render_and_request(self, prompt: "Prompt", kind: str = "create") -> str: + def render_and_request( + self, prompt: "Prompt", kind: str = "create", dedupe_enabled: bool = True + ) -> str: if not self.client: raise ValueError("Prompt has no client set.") + prompt.dedupe_enabled = dedupe_enabled + loop = asyncio.get_event_loop() return loop.run_until_complete(prompt.send(self.client, kind=kind)) @@ -483,9 +496,15 @@ class Prompt: ] ) - def query_text(self, query: str, text: str, as_question_answer: bool = True): + def query_text( + self, + query: str, + text: str, + as_question_answer: bool = True, + short: bool = False, + ): loop = asyncio.get_event_loop() - summarizer = instance.get_agent("world_state") + world_state = instance.get_agent("world_state") query = query.format(**self.vars) if isinstance(text, list): @@ -493,7 +512,7 @@ class Prompt: if not as_question_answer: return loop.run_until_complete( - summarizer.analyze_text_and_answer_question(text, query) + world_state.analyze_text_and_answer_question(text, query, short=short) ) return "\n".join( @@ -501,11 +520,18 @@ class Prompt: f"Question: {query}", f"Answer: " + loop.run_until_complete( - summarizer.analyze_text_and_answer_question(text, query) + world_state.analyze_text_and_answer_question( + text, query, short=short + ) ), ] ) + def query_text_eval(self, query: str, text: str): + query = f"{query} Answer with a yes or no." + response = self.query_text(query, text, as_question_answer=False, short=True) + return response.strip().lower().startswith("y") + def query_memory(self, query: str, as_question_answer: bool = True, **kwargs): loop = asyncio.get_event_loop() memory = instance.get_agent("memory") @@ -551,14 +577,17 @@ class Prompt: world_state.analyze_text_and_extract_context("\n".join(lines), goal=goal) ) - def agent_action(self, agent_name: str, action_name: str, **kwargs): + def agent_action(self, agent_name: str, _action_name: str, **kwargs): loop = asyncio.get_event_loop() agent = instance.get_agent(agent_name) - action = getattr(agent, action_name) + action = getattr(agent, _action_name) return loop.run_until_complete(action(**kwargs)) - def emit_status(self, status: str, message: str): - emit("status", status=status, message=message) + def emit_status(self, status: str, message: str, **kwargs): + if kwargs: + emit("status", status=status, message=message, data=kwargs) + else: + emit("status", status=status, message=message) def set_prepared_response(self, response: str, prepend: str = ""): """ diff --git a/src/talemate/prompts/templates/conversation/dialogue.jinja2 b/src/talemate/prompts/templates/conversation/dialogue.jinja2 index da6adcdc..90ba951d 100644 --- a/src/talemate/prompts/templates/conversation/dialogue.jinja2 +++ b/src/talemate/prompts/templates/conversation/dialogue.jinja2 @@ -84,8 +84,12 @@ Always contain dialogue in quotation marks. For example, {{ talking_character.na <|SECTION:SCENE|> {% endblock -%} {% block scene_history -%} -{% for scene_context in scene.context_history(budget=max_tokens-200-count_tokens(self.rendered_context()), min_dialogue=15, sections=False, keep_director=talking_character.name) -%} -{{ scene_context }} +{% set scene_context = scene.context_history(budget=max_tokens-200-count_tokens(self.rendered_context()), min_dialogue=15, sections=False, keep_director=talking_character.name) -%} +{%- if talking_character.dialogue_instructions -%} +{% set _ = scene_context.insert(-3, "# Internal acting instructions for "+talking_character.name+": "+talking_character.dialogue_instructions) %} +{% endif -%} +{% for scene_line in scene_context -%} +{{ scene_line }} {% endfor %} {% endblock -%} <|CLOSE_SECTION|> @@ -93,10 +97,11 @@ Always contain dialogue in quotation marks. For example, {{ talking_character.na {% endif -%} {% if rerun_context and rerun_context.direction -%} {% if rerun_context.method == 'replace' -%} -Final instructions for generating the next line of dialogue: {{ rerun_context.direction }} +# Final instructions for generating the next line of dialogue: {{ rerun_context.direction }} {% elif rerun_context.method == 'edit' and rerun_context.message -%} -Edit and respond with your changed version of the following line of dialogue: {{ rerun_context.message }} -Requested changes: {{ rerun_context.direction }} +# Edit and respond with your changed version of the following line of dialogue: {{ rerun_context.message|condensed }} + +# Requested changes: {{ rerun_context.direction }} {% endif -%} {% endif -%} {{ bot_token}}{{ talking_character.name }}:{{ partial_message }} \ No newline at end of file diff --git a/src/talemate/prompts/templates/creator/contextual-generate.jinja2 b/src/talemate/prompts/templates/creator/contextual-generate.jinja2 new file mode 100644 index 00000000..e36b9f5e --- /dev/null +++ b/src/talemate/prompts/templates/creator/contextual-generate.jinja2 @@ -0,0 +1,71 @@ +{% block rendered_context %} +{% include "extra-context.jinja2" %} +{% if character %} +<|SECTION:CHARACTER|> +{% if context_typ == 'character attribute' -%} +{{ character.sheet_filtered(context_name) }} +{% else -%} +{{ character.sheet }} +{% endif -%} +<|CLOSE_SECTION|> +{% endif %} +{% endblock %} +<|SECTION:SCENE|> +{% for scene_context in scene.context_history(budget=max_tokens-1024-count_tokens(self.rendered_context())) -%} +{{ scene_context }} +{% endfor %} +<|CLOSE_SECTION|> +<|SECTION:TASK|> +{#- SET TASK ACTION -#} +{% if not generation_context.original %} + {%- set action_task = "Generate the" -%} +{% else %} + {%- set action_task = "Rewrite the existing" -%} + Original {{ context_name }}: {{ generation_context.original }} +{% endif %} +{#- CHARACTER ATTRIBUTE -#} +{% if context_typ == "character attribute" %} +{{ action_task }} "{{ context_name }}" attribute for {{ character.name }}. This must be a general description and not a continuation of the current narrative. +{#- CHARACTER DETAIL -#} +{% elif context_typ == "character detail" %} +{% if context_name.endswith("?") -%} +{{ action_task }} answer to "{{ context_name }}" for {{ character.name }}. This must be a general description and not a continuation of the current narrative. +{% else -%} +{{ action_task }} "{{ context_name }}" detail for {{ character.name }}. This must be a general description and not a continuation of the current narrative. Use paragraphs to separate different details. +{% endif -%} +Use a simple, easy to read writing format. +{#- CHARACTER EXAMPLE DIALOGUE -#} +{% elif context_typ == "character dialogue" %} +Generate a new line of example dialogue for {{ character.name }}. + +Exisiting Dialogue Examples: +{% for line in character.example_dialogue %} +{{ line }} +{% endfor %} + +You must only respond with the generated dialogue example. +Always contain actions in asterisks. For example, *{{ character.name}} smiles*. +Always contain dialogue in quotation marks. For example, {{ character.name}}: "Hello!" + +{%- if character.dialogue_instructions -%} +Dialogue instructions for {{ character.name }}: {{ character.dialogue_instructions }} +{% endif -%} +{#- GENERAL CONTEXT -#} +{% else %} +{% if context_name.endswith("?") -%} +{{ action_task }} answer to the question "{{ context_name }}". This must be a general description and not a continuation of the current narrative. +{%- else -%} +{{ action_task }} new narrative content for {{ context_name }} +Use a simple, easy to read writing format. +{%- endif -%} +{% endif %} +{% if generation_context.instructions %}Additional instructions: {{ generation_context.instructions }}{% endif %} +<|CLOSE_SECTION|> +{{ bot_token }} +{%- if context_typ == 'character attribute' -%} +{{ character.name }}'s {{ context_name }}: +{%- elif context_typ == 'character dialogue' -%} +{{ character.name }}: +{%- else -%} +{{ context_name }}: +{%- endif -%} \ No newline at end of file diff --git a/src/talemate/prompts/templates/creator/determine-character-name.jinja2 b/src/talemate/prompts/templates/creator/determine-character-name.jinja2 new file mode 100644 index 00000000..16029244 --- /dev/null +++ b/src/talemate/prompts/templates/creator/determine-character-name.jinja2 @@ -0,0 +1,21 @@ +{% block rendered_context %} +{% include "extra-context.jinja2" %} +{% endblock %} +<|SECTION:SCENE|> +{% for scene_context in scene.context_history(budget=max_tokens-1024-count_tokens(self.rendered_context())) -%} +{{ scene_context }} +{% endfor %} +<|CLOSE_SECTION|> +<|SECTION:TASK|> +Determine character name based on the following sentence: {{ character_name }} + +{% if not allowed_names -%} +If the character already has a distinct name, respond with the character's name. +If the name is currently a description, give the character a distinct name. +If we don't know the character's actual name, you must decide one. +YOU MUST ONLY RESPOND WITH THE CHARACTER NAME, NOTHING ELSE. +{% else %} +Pick the most fitting name from the following list: {{ allowed_names|join(', ') }}. If none of the names fit, respond with the most accurate name based on the sentence. +{%- endif %} +<|CLOSE_SECTION|> +{{ bot_token }}The character's name is " \ No newline at end of file diff --git a/src/talemate/prompts/templates/creator/extra-context.jinja2 b/src/talemate/prompts/templates/creator/extra-context.jinja2 new file mode 100644 index 00000000..71aebe53 --- /dev/null +++ b/src/talemate/prompts/templates/creator/extra-context.jinja2 @@ -0,0 +1,29 @@ +Scenario Premise: +{{ scene.description }} + +Content Context: This is a specific scene from {{ scene.context }} + +{% block rendered_context_static %} +{# GENERAL REINFORCEMENTS #} +{% set general_reinforcements = scene.world_state.filter_reinforcements(insert=['all-context']) %} +{%- for reinforce in general_reinforcements %} +{{ reinforce.as_context_line|condensed }} + +{% endfor %} +{# END GENERAL REINFORCEMENTS #} +{# ACTIVE PINS #} +{%- for pin in scene.active_pins %} +{{ pin.time_aware_text|condensed }} + +{% endfor %} +{# END ACTIVE PINS #} +{% endblock %} + +{# MEMORY #} +{%- if memory_query %} +{%- for memory in query_memory(memory_query, as_question_answer=False, max_tokens=max_tokens-500-count_tokens(self.rendered_context_static()), iterate=10) -%} +{{ memory|condensed }} + +{% endfor -%} +{% endif -%} +{# END MEMORY #} \ No newline at end of file diff --git a/src/talemate/prompts/templates/narrator/narrate-progress.jinja2 b/src/talemate/prompts/templates/narrator/narrate-progress.jinja2 index c1e5c94b..7beb50f9 100644 --- a/src/talemate/prompts/templates/narrator/narrate-progress.jinja2 +++ b/src/talemate/prompts/templates/narrator/narrate-progress.jinja2 @@ -14,6 +14,8 @@ Player Character: {{ player_character.name }} {% endfor %} <|CLOSE_SECTION|> <|SECTION:TASK|> +YOU MUST WRITE FROM THE PERSPECTIVE OF THE NARRATOR. + Continue the current dialogue by narrating the progression of the scene. If the scene is over, narrate the beginning of the next scene. @@ -28,6 +30,8 @@ Use an informal and colloquial register with a conversational tone. Overall, the Narration style should be that of a 90s point and click adventure game. You are omniscient and can describe the scene in detail. +YOU MUST WRITE FROM THE PERSPECTIVE OF THE NARRATOR. + Only generate new narration. Avoid including any character's internal thoughts or dialogue. {% if narrative_direction %} @@ -36,5 +40,4 @@ Directions for new narration: {{ narrative_direction }} Write 2 to 4 sentences. {{ extra_instructions }} {% include "rerun-context.jinja2" -%} -<|CLOSE_SECTION|> -{{ set_prepared_response("*") }} \ No newline at end of file +<|CLOSE_SECTION|> \ No newline at end of file diff --git a/src/talemate/prompts/templates/narrator/paraphrase.jinja2 b/src/talemate/prompts/templates/narrator/paraphrase.jinja2 new file mode 100644 index 00000000..0f239198 --- /dev/null +++ b/src/talemate/prompts/templates/narrator/paraphrase.jinja2 @@ -0,0 +1,20 @@ +{% block rendered_context -%} +<|SECTION:CONTEXT|> +{% include "extra-context.jinja2" %} +<|CLOSE_SECTION|> +{% endblock -%} +<|SECTION:SCENE|> +{% for scene_context in scene.context_history(budget=max_tokens-300-count_tokens(self.rendered_context())) -%} +{{ scene_context }} +{% endfor %} +<|CLOSE_SECTION|> +<|SECTION:TASK|> +Paraphrase the following text to make it fit the narrative tone. Keep the information and the meaning the same, but change the wording and sentence structure. + +Text to paraphrase: + +"{{ text }}" + +{{ extra_instructions }} +{% include "rerun-context.jinja2" -%} +<|CLOSE_SECTION|> \ No newline at end of file diff --git a/src/talemate/prompts/templates/world_state/extract-character-sheet.jinja2 b/src/talemate/prompts/templates/world_state/extract-character-sheet.jinja2 index e9e12e35..301af1da 100644 --- a/src/talemate/prompts/templates/world_state/extract-character-sheet.jinja2 +++ b/src/talemate/prompts/templates/world_state/extract-character-sheet.jinja2 @@ -11,10 +11,11 @@ {{ text }} {% endif -%} <|SECTION:TASK|> +{% if not character %} Generate a real world character profile for {{ name }}, one attribute per line. You are a creative writer and are allowed to fill in any gaps in the profile with your own ideas. - Expand on interesting details. + Narration style should be that of a 90s point and click adventure game. You are omniscient and can describe the scene in detail. Use an informal and colloquial register with a conversational tone. Overall, the narrative is Informal, conversational, natural, and spontaneous, with a sense of immediacy. @@ -27,5 +28,12 @@ Appearance: <...> Format MUST be one attribute per line, with a colon after the attribute name. +{% else %} +{{ character.sheet }} +Update the character sheet with any realtime changes for {{ name }} based on the context and the following information. Add one attribute per line. You are a creative writer and are allowed to fill in any gaps in the profile with your own ideas. + +Alteration instructions: {{ alteration_instructions }} + +{% endif %} {{ set_prepared_response("Name: "+name+"\nAge:") }} \ No newline at end of file diff --git a/src/talemate/scene_message.py b/src/talemate/scene_message.py index 600e8804..d84489a3 100644 --- a/src/talemate/scene_message.py +++ b/src/talemate/scene_message.py @@ -18,7 +18,6 @@ def reset_message_id(): @dataclass class SceneMessage: - """ Base class for all messages that are sent to the scene. """ @@ -32,6 +31,8 @@ class SceneMessage: # the source of the message (e.g. "ai", "progress_story", "director") source: str = "" + hidden: bool = False + typ = "scene" def __str__(self): @@ -73,6 +74,16 @@ class SceneMessage: def secondary_source(self): return self.source + @property + def raw(self): + return str(self.message) + + def hide(self): + self.hidden = True + + def unhide(self): + self.hidden = False + @dataclass class CharacterMessage(SceneMessage): @@ -90,6 +101,10 @@ class CharacterMessage(SceneMessage): def secondary_source(self): return self.character_name + @property + def raw(self): + return self.message.split(":", 1)[1].replace('"', "").replace("*", "").strip() + @dataclass class NarratorMessage(SceneMessage): @@ -110,7 +125,7 @@ class DirectorMessage(SceneMessage): transformed_message = self.message.replace("Director instructs ", "") char_name, message = transformed_message.split(":", 1) - return f"[Story progression instructions for {char_name}: {message}]" + return f"# Story progression instructions for {char_name}: {message}" @dataclass @@ -135,7 +150,7 @@ class ReinforcementMessage(SceneMessage): def __str__(self): question, _ = self.source.split(":", 1) - return f"[Context state: {question}: {self.message}]" + return f"# Internal notes: {question}: {self.message}" MESSAGES = { diff --git a/src/talemate/server/assistant.py b/src/talemate/server/assistant.py new file mode 100644 index 00000000..818074ad --- /dev/null +++ b/src/talemate/server/assistant.py @@ -0,0 +1,43 @@ +import pydantic +import structlog + +from talemate.agents.creator.assistant import ContentGenerationContext +from talemate.instance import get_agent + +log = structlog.get_logger("talemate.server.assistant") + + +class AssistantPlugin: + router = "assistant" + + @property + def scene(self): + return self.websocket_handler.scene + + def __init__(self, websocket_handler): + self.websocket_handler = websocket_handler + + async def handle(self, data: dict): + log.info("assistant action", action=data.get("action")) + + fn = getattr(self, f"handle_{data.get('action')}", None) + + if fn is None: + return + + await fn(data) + + async def handle_contextual_generate(self, data: dict): + payload = ContentGenerationContext(**data) + creator = get_agent("creator") + content = await creator.contextual_generate(payload) + self.websocket_handler.queue_put( + { + "type": self.router, + "action": "contextual_generate_done", + "data": { + "generated_content": content, + **payload.model_dump(), + }, + } + ) diff --git a/src/talemate/server/devtools.py b/src/talemate/server/devtools.py new file mode 100644 index 00000000..71f361d9 --- /dev/null +++ b/src/talemate/server/devtools.py @@ -0,0 +1,51 @@ +import pydantic +import structlog + +log = structlog.get_logger("talemate.server.devtools") + + +class TestPromptPayload(pydantic.BaseModel): + prompt: str + generation_parameters: dict + client_name: str + kind: str + + +class DevToolsPlugin: + router = "devtools" + + def __init__(self, websocket_handler): + self.websocket_handler = websocket_handler + + async def handle(self, data: dict): + log.info("Config action", action=data.get("action")) + + fn = getattr(self, f"handle_{data.get('action')}", None) + + if fn is None: + return + + await fn(data) + + async def handle_test_prompt(self, data): + payload = TestPromptPayload(**data) + client = self.websocket_handler.llm_clients[payload.client_name]["client"] + response = await client.generate( + payload.prompt, + payload.generation_parameters, + payload.kind, + ) + + self.websocket_handler.queue_put( + { + "type": "devtools", + "action": "test_prompt_response", + "data": { + "prompt": payload.prompt, + "generation_parameters": payload.generation_parameters, + "client_name": payload.client_name, + "kind": payload.kind, + "response": response, + }, + } + ) diff --git a/src/talemate/server/run.py b/src/talemate/server/run.py index 8331eaa9..d22413bf 100644 --- a/src/talemate/server/run.py +++ b/src/talemate/server/run.py @@ -6,6 +6,7 @@ import sys import structlog import websockets +import talemate.config from talemate.server.api import websocket_endpoint log = structlog.get_logger("talemate.server.run") @@ -17,6 +18,12 @@ def run_server(args): :param args: command line arguments parsed by argparse """ + + import talemate.agents.custom + import talemate.client.custom + + talemate.config.cleanup() + start_server = websockets.serve( websocket_endpoint, args.host, args.port, max_size=2**23 ) diff --git a/src/talemate/server/websocket_server.py b/src/talemate/server/websocket_server.py index d5b1d730..765f5fcc 100644 --- a/src/talemate/server/websocket_server.py +++ b/src/talemate/server/websocket_server.py @@ -18,9 +18,11 @@ from talemate.load import ( ) from talemate.scene_assets import Asset from talemate.server import ( + assistant, character_creator, character_importer, config, + devtools, quick_settings, scene_creator, world_state_manager, @@ -55,6 +57,7 @@ class WebsocketHandler(Receiver): self.connect_llm_clients() self.routes = { + assistant.AssistantPlugin.router: assistant.AssistantPlugin(self), character_creator.CharacterCreatorServerPlugin.router: character_creator.CharacterCreatorServerPlugin( self ), @@ -71,6 +74,7 @@ class WebsocketHandler(Receiver): quick_settings.QuickSettingsPlugin.router: quick_settings.QuickSettingsPlugin( self ), + devtools.DevToolsPlugin.router: devtools.DevToolsPlugin(self), } # self.request_scenes_list() @@ -582,17 +586,20 @@ class WebsocketHandler(Receiver): def request_scene_assets(self, asset_ids: list[str]): scene_assets = self.scene.assets - for asset_id in asset_ids: - asset = scene_assets.get_asset_bytes_as_base64(asset_id) + try: + for asset_id in asset_ids: + asset = scene_assets.get_asset_bytes_as_base64(asset_id) - self.queue_put( - { - "type": "scene_asset", - "asset_id": asset_id, - "asset": asset, - "media_type": scene_assets.get_asset(asset_id).media_type, - } - ) + self.queue_put( + { + "type": "scene_asset", + "asset_id": asset_id, + "asset": asset, + "media_type": scene_assets.get_asset(asset_id).media_type, + } + ) + except Exception as exc: + log.error("request_scene_assets", error=traceback.format_exc()) def request_assets(self, assets: list[dict]): # way to request scene assets without loading the scene diff --git a/src/talemate/server/world_state_manager.py b/src/talemate/server/world_state_manager.py index 243e0eec..c52eabf2 100644 --- a/src/talemate/server/world_state_manager.py +++ b/src/talemate/server/world_state_manager.py @@ -41,6 +41,12 @@ class CharacterDetailReinforcementPayload(pydantic.BaseModel): reset: bool = False +class CharacterActorPayload(pydantic.BaseModel): + name: str + dialogue_instructions: str + dialogue_examples: list[str] = pydantic.Field(default_factory=list) + + class SaveWorldEntryPayload(pydantic.BaseModel): id: str text: str @@ -312,6 +318,27 @@ class WorldStateManagerPlugin: await self.handle_get_character_details({"name": payload.name}) await self.signal_operation_done() + async def handle_update_character_actor(self, data): + payload = CharacterActorPayload(**data) + + await self.world_state_manager.update_character_actor( + payload.name, + payload.dialogue_instructions, + payload.dialogue_examples, + ) + + self.websocket_handler.queue_put( + { + "type": "world_state_manager", + "action": "character_actor_updated", + "data": payload.model_dump(), + } + ) + + # resend character details + await self.handle_get_character_details({"name": payload.name}) + await self.signal_operation_done() + async def handle_save_world_entry(self, data): payload = SaveWorldEntryPayload(**data) diff --git a/src/talemate/tale_mate.py b/src/talemate/tale_mate.py index 86c99bbd..974a57f1 100644 --- a/src/talemate/tale_mate.py +++ b/src/talemate/tale_mate.py @@ -3,12 +3,12 @@ import json import os import random import re -import time import traceback import uuid -from typing import Dict, List, Optional, Union +from typing import Dict, Generator, List, Union import isodate +import pydantic import structlog from blinker import signal @@ -98,6 +98,9 @@ class Character: self.base_attributes = base_attributes or {} self.details = details or {} self.cover_image = kwargs.get("cover_image") + self.dialogue_instructions = kwargs.get("dialogue_instructions") + + self.memory_dirty = False @property def persona(self): @@ -117,10 +120,11 @@ class Character: "history_events": self.history_events, "is_player": self.is_player, "cover_image": self.cover_image, + "dialogue_instructions": self.dialogue_instructions, } @property - def sheet(self): + def sheet(self) -> str: sheet = self.base_attributes or { "name": self.name, "gender": self.gender, @@ -147,6 +151,22 @@ class Character: return random.choice(self.example_dialogue) + def sheet_filtered(self, *exclude): + + sheet = self.base_attributes or { + "name": self.name, + "gender": self.gender, + "description": self.description, + } + + sheet_list = [] + + for key, value in sheet.items(): + if key not in exclude: + sheet_list.append(f"{key}: {value}") + + return "\n".join(sheet_list) + def random_dialogue_examples(self, num: int = 3): """ Get multiple random example dialogue lines for this character. @@ -326,6 +346,14 @@ class Character: for i, dialogue in enumerate(self.example_dialogue): self.example_dialogue[i] = pattern.sub(character.name, dialogue) + def update(self, **kwargs): + """ + Update character properties with given key-value pairs. + """ + + for key, value in kwargs.items(): + setattr(self, key, value) + async def commit_to_memory(self, memory_agent): """ Commits this character's details to the memory agent. (vectordb) @@ -408,6 +436,8 @@ class Character: if items: await memory_agent.add_many(items) + self.memory_dirty = False + async def commit_single_attribute_to_memory( self, memory_agent, attribute: str, value: str ): @@ -488,6 +518,10 @@ class Character: self.details[name] = value await self.commit_single_detail_to_memory(memory_agent, name, value) + def set_detail_defer(self, name: str, value): + self.details[name] = value + self.memory_dirty = True + def get_detail(self, name: str): return self.details.get(name) @@ -506,6 +540,10 @@ class Character: self.base_attributes[name] = value await self.commit_single_attribute_to_memory(memory_agent, name, value) + def set_base_attribute_defer(self, name: str, value): + self.base_attributes[name] = value + self.memory_dirty = True + def get_base_attribute(self, name: str): return self.base_attributes.get(name) @@ -655,6 +693,9 @@ class Player(Actor): return if not commands.Manager.is_command(message): + if '"' not in message and "*" not in message: + message = f'"{message}"' + message = util.ensure_dialog_format(message) self.message = message @@ -701,11 +742,15 @@ class Scene(Emitter): self.max_tokens = 2048 self.next_actor = None + self.experimental = False + self.help = "" + self.name = "" self.filename = "" self.memory_id = str(uuid.uuid4())[:10] self.saved_memory_session_id = None self.memory_session_id = str(uuid.uuid4())[:10] + self.restore_from = None # has scene been saved before? self.saved = False @@ -984,6 +1029,15 @@ class Scene(Emitter): return idx return -1 + def last_player_message(self) -> str: + """ + Returns the last message from the player + """ + for idx in range(len(self.history) - 1, -1, -1): + if isinstance(self.history[idx], CharacterMessage): + if self.history[idx].source == "player": + return self.history[idx] + def collect_messages( self, typ: str = None, source: str = None, max_iterations: int = 100 ): @@ -1112,6 +1166,8 @@ class Scene(Emitter): if _actor == actor: self.actors.remove(_actor) + actor.character = None + def add_helper(self, helper: Helper): """ Add a helper to the scene @@ -1133,6 +1189,9 @@ class Scene(Emitter): Returns the character with the given name if it exists """ + if not character_name: + return + if character_name in self.inactive_characters: return self.inactive_characters[character_name] @@ -1154,10 +1213,10 @@ class Scene(Emitter): if not isinstance(actor, Player): yield actor.character - def num_npc_characters(self): + def num_npc_characters(self) -> int: return len(list(self.get_npc_characters())) - def get_characters(self): + def get_characters(self) -> Generator[Character, None, None]: """ Returns a list of all characters in the scene """ @@ -1267,22 +1326,21 @@ class Scene(Emitter): for i in range(len(self.history) - 1, -1, -1): count += 1 - if isinstance(self.history[i], DirectorMessage): + message = self.history[i] + + if message.hidden: + continue + + if isinstance(message, DirectorMessage): if not keep_director: continue - elif ( - isinstance(keep_director, str) - and self.history[i].source != keep_director - ): + elif isinstance(keep_director, str) and message.source != keep_director: continue - if ( - count_tokens(parts_dialogue) + count_tokens(self.history[i]) - > budget_dialogue - ): + if count_tokens(parts_dialogue) + count_tokens(message) > budget_dialogue: break - parts_dialogue.insert(0, self.history[i]) + parts_dialogue.insert(0, message) # collect context, ignore where end > len(history) - count @@ -1397,6 +1455,8 @@ class Scene(Emitter): director = self.get_helper("director").agent await director.direct_scene(None, None) return + elif source == "paraphrase": + new_message = await narrator.agent.paraphrase(arg) else: fn = getattr(narrator.agent, source, None) if not fn: @@ -1508,22 +1568,26 @@ class Scene(Emitter): data={ "environment": self.environment, "scene_config": self.scene_config, - "player_character_name": player_character.name - if player_character - else None, + "player_character_name": ( + player_character.name if player_character else None + ), "inactive_characters": list(self.inactive_characters.keys()), "context": self.context, "assets": self.assets.dict(), "characters": [actor.character.serialize for actor in self.actors], - "scene_time": util.iso8601_duration_to_human(self.ts, suffix="") - if self.ts - else None, + "scene_time": ( + util.iso8601_duration_to_human(self.ts, suffix="") + if self.ts + else None + ), "saved": self.saved, "auto_save": self.auto_save, "auto_progress": self.auto_progress, "can_auto_save": self.can_auto_save(), "game_state": self.game_state.model_dump(), "active_pins": [pin.model_dump() for pin in self.active_pins], + "experimental": self.experimental, + "help": self.help, }, ) @@ -1657,12 +1721,13 @@ class Scene(Emitter): async def _run_game_loop(self, init: bool = True): if init: + emit("clear_screen", "") + self.game_state.init(self) + await self.signals["scene_init"].send( events.SceneStateEvent(scene=self, event_type="scene_init") ) - - emit("clear_screen", "") self.narrator_message(self.get_intro()) for actor in self.actors: @@ -1731,6 +1796,16 @@ class Scene(Emitter): signal_game_loop = True for actor in self.actors: + + if not actor.character: + self.log.warning("Actor has no character", actor=actor) + continue + + if actor.character.memory_dirty: + await actor.character.commit_to_memory( + self.get_helper("memory").agent + ) + if not self.auto_progress and not actor.character.is_player: # auto progress is disabled, so NPCs don't get automatic turns continue @@ -1928,6 +2003,8 @@ class Scene(Emitter): "saved_memory_session_id": scene.saved_memory_session_id, "immutable_save": scene.immutable_save, "ts": scene.ts, + "help": scene.help, + "experimental": scene.experimental, } if not auto: @@ -1982,3 +2059,72 @@ class Scene(Emitter): self.archived_history = [] self.filename = "" self.goal = None + + async def remove_all_actors(self): + for actor in self.actors: + actor.character = None + + self.actors = [] + + async def restore(self): + try: + self.log.info("Restoring", source=self.restore_from) + + if not self.restore_from: + self.log.error("No restore_from set") + return + + self.reset() + self.inactive_characters = {} + await self.remove_all_actors() + + from talemate.load import load_scene + + await load_scene( + self, + os.path.join(self.save_dir, self.restore_from), + self.get_helper("conversation").agent.client, + ) + + self.emit_status() + except Exception as e: + self.log.error("restore", error=e, traceback=traceback.format_exc()) + + def sync_restore(self): + loop = asyncio.get_event_loop() + loop.run_until_complete(self.restore()) + + @property + def serialize(self): + scene = self + return { + "description": scene.description, + "intro": scene.intro, + "name": scene.name, + "history": scene.history, + "environment": scene.environment, + "archived_history": scene.archived_history, + "character_states": scene.character_states, + "characters": [actor.character.serialize for actor in scene.actors], + "inactive_characters": { + name: character.serialize + for name, character in scene.inactive_characters.items() + }, + "goal": scene.goal, + "goals": scene.goals, + "context": scene.context, + "world_state": scene.world_state.model_dump(), + "game_state": scene.game_state.model_dump(), + "assets": scene.assets.dict(), + "memory_id": scene.memory_id, + "memory_session_id": scene.memory_session_id, + "saved_memory_session_id": scene.saved_memory_session_id, + "immutable_save": scene.immutable_save, + "ts": scene.ts, + "help": scene.help, + "experimental": scene.experimental, + } + + @property + def json(self): + return json.dumps(self.serialize, indent=2, cls=save.SceneEncoder) diff --git a/src/talemate/util.py b/src/talemate/util.py index b6773a8b..e89fe437 100644 --- a/src/talemate/util.py +++ b/src/talemate/util.py @@ -283,6 +283,29 @@ def replace_conditional(input_string: str, params) -> str: def strip_partial_sentences(text: str) -> str: + """ + Removes any unfinished sentences from the end of the input text. + + This new version works backwards and doesnt destroy string formatting (newlines etc.) + + Args: + text (str): The input text to be cleaned. + + Returns: + str: The cleaned text. + """ + sentence_endings = [".", "!", "?", '"', "*"] + + # loop backwards through `text` until a sentence ending is found + + for i in range(len(text) - 1, -1, -1): + if text[i] in sentence_endings: + return text[: i + 1] + + return text + + +def strip_partial_sentences_old(text: str) -> str: # Sentence ending characters sentence_endings = [".", "!", "?", '"', "*"] diff --git a/src/talemate/world_state/__init__.py b/src/talemate/world_state/__init__.py index a1c98181..c4f48151 100644 --- a/src/talemate/world_state/__init__.py +++ b/src/talemate/world_state/__init__.py @@ -3,9 +3,8 @@ from enum import Enum from typing import Any, Union import structlog -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel -import talemate.automated_action as automated_action import talemate.instance as instance from talemate.emit import emit from talemate.prompts import Prompt @@ -86,6 +85,8 @@ class WorldState(BaseModel): # manual context manual_context: dict[str, ManualContext] = {} + character_name_mappings: dict[str, list[str]] = {} + @property def agent(self): return instance.get_agent("world_state") @@ -102,6 +103,9 @@ class WorldState(BaseModel): def as_list(self): return self.render().as_list + def add_character_name_mappings(self, *names): + self.character_name_mappings.extend([name.lower() for name in names]) + def filter_reinforcements( self, character: str = ANY_CHARACTER, insert: list[str] = None ) -> list[Reinforcement]: @@ -188,6 +192,20 @@ class WorldState(BaseModel): self.items = {} for character_name, character in world_state.get("characters", {}).items(): + + # if character name is an alias, we need to convert it to the main name + # if it exists in the mappings + + for main_name, synonyms in self.character_name_mappings.items(): + if character_name.lower() in synonyms: + log.debug( + "world_state adjusting character name (via mapping)", + from_name=character_name, + to_name=main_name, + ) + character_name = main_name + break + # character name may not always come back exactly as we have # it defined in the scene. We assign the correct name by checking occurences # of both names in each other. @@ -221,14 +239,28 @@ class WorldState(BaseModel): ) if character_name in previous_characters: character["emotion"] = previous_characters[character_name].emotion + try: + self.characters[character_name] = CharacterState(**character) + except Exception as e: + log.error( + "world_state.request_update", + error=e, + traceback=traceback.format_exc(), + ) - self.characters[character_name] = CharacterState(**character) log.debug("world_state", character=character) for item_name, item in world_state.get("items", {}).items(): if not item: continue - self.items[item_name] = ObjectState(**item) + try: + self.items[item_name] = ObjectState(**item) + except Exception as e: + log.error( + "world_state.request_update", + error=e, + traceback=traceback.format_exc(), + ) log.debug("world_state", item=item) # deactivate persiting for now diff --git a/src/talemate/world_state/manager.py b/src/talemate/world_state/manager.py index 6df5a48a..e08aa4e6 100644 --- a/src/talemate/world_state/manager.py +++ b/src/talemate/world_state/manager.py @@ -1,8 +1,9 @@ -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Union import pydantic import structlog +from talemate.character import activate_character, deactivate_character from talemate.config import StateReinforcementTemplate, WorldStateTemplates, save_config from talemate.instance import get_agent from talemate.world_state import ContextPin, InsertionMode, ManualContext, Reinforcement @@ -29,6 +30,11 @@ class ContextDB(pydantic.BaseModel): entries: list[ContextDBEntry] = [] +class CharacterActor(pydantic.BaseModel): + dialogue_examples: list[str] = pydantic.Field(default_factory=list) + dialogue_instructions: Union[str, None] = None + + class CharacterDetails(pydantic.BaseModel): name: str active: bool = True @@ -37,6 +43,7 @@ class CharacterDetails(pydantic.BaseModel): base_attributes: dict[str, str] = {} details: dict[str, str] = {} reinforcements: dict[str, Reinforcement] = {} + actor: CharacterActor = pydantic.Field(default_factory=CharacterActor) class World(pydantic.BaseModel): @@ -130,13 +137,21 @@ class WorldStateManager: active=True, description=character.description, is_player=character.is_player, + actor=CharacterActor( + dialogue_examples=character.example_dialogue, + dialogue_instructions=character.dialogue_instructions, + ), ) - for key, value in character.base_attributes.items(): - details.base_attributes[key] = value + # sorted base attributes + for key in sorted(character.base_attributes.keys()): + if key.startswith("_"): + continue + details.base_attributes[key] = character.base_attributes[key] - for key, value in character.details.items(): - details.details[key] = value + # sorted details + for key in sorted(character.details.keys()): + details.details[key] = character.details[key] details.reinforcements = self.world_state.reinforcements_for_character( character_name @@ -263,6 +278,38 @@ class WorldStateManager: character = self.scene.get_character(character_name) await character.set_description(description) + async def update_character_actor( + self, + character_name: str, + dialogue_instructions: str = None, + example_dialogue: list[str] = None, + ): + """ + Sets the actor for a character. + + Arguments: + character_name: The name of the character whose actor is to be set. + dialogue_instructions: The dialogue instructions for the character. + example_dialogue: A list of example dialogue for the character. + """ + log.debug( + "update_character_actor", + character_name=character_name, + dialogue_instructions=dialogue_instructions, + example_dialogue=example_dialogue, + ) + character = self.scene.get_character(character_name) + character.dialogue_instructions = dialogue_instructions + + # make sure every example dialogue starts with {character_name}: + + if example_dialogue: + for idx, example in enumerate(example_dialogue): + if not example.startswith(f"{character_name}:"): + example_dialogue[idx] = f"{character_name}: {example}" + + character.example_dialogue = example_dialogue + async def add_detail_reinforcement( self, character_name: str, @@ -334,7 +381,9 @@ class WorldStateManager: await self.world_state.remove_reinforcement(idx) self.world_state.emit() - async def save_world_entry(self, entry_id: str, text: str, meta: dict): + async def save_world_entry( + self, entry_id: str, text: str, meta: dict, pin: bool = False + ): """ Saves a manual world entry with specified text and metadata. @@ -347,6 +396,9 @@ class WorldStateManager: meta["typ"] = "world_state" await self.update_context_db_entry(entry_id, text, meta) + if pin: + await self.set_pin(entry_id, active=True) + async def update_context_db_entry(self, entry_id: str, text: str, meta: dict): """ Updates an entry in the context database with new text and metadata. @@ -556,7 +608,7 @@ class WorldStateManager: async def apply_template_state_reinforcement( self, - template: StateReinforcementTemplate, + template: Union[str, StateReinforcementTemplate], character_name: str = None, run_immediately: bool = False, ) -> Reinforcement: @@ -564,7 +616,7 @@ class WorldStateManager: Applies a state reinforcement template to a specific character, if provided. Arguments: - template: The StateReinforcementTemplate object defining the reinforcement details. + template: The StateReinforcementTemplate object defining the reinforcement details. Can also be a string representing the template name. character_name: Optional; the name of the character to apply the template to. run_immediately: Whether to run the reinforcement immediately after applying. @@ -575,6 +627,11 @@ class WorldStateManager: ValueError: If a character name is required but not provided. """ + if isinstance(template, str): + template = (await self.get_templates()).get_template(template) + if not template: + return + if not character_name and template.state_type in ["npc", "character", "player"]: raise ValueError("Character name required for this template type.") @@ -606,3 +663,21 @@ class WorldStateManager: insert=template.insert, run_immediately=run_immediately, ) + + async def activate_character(self, character_name: str): + """ + Activates a character in the scene. + + Arguments: + character_name: The name of the character to activate. + """ + await activate_character(self.scene, character_name) + + async def deactivate_character(self, character_name: str): + """ + Deactivates a character in the scene. + + Arguments: + character_name: The name of the character to deactivate. + """ + await deactivate_character(self.scene, character_name) diff --git a/talemate_frontend/package-lock.json b/talemate_frontend/package-lock.json index 11ab58fe..f82c888e 100644 --- a/talemate_frontend/package-lock.json +++ b/talemate_frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "talemate_frontend", - "version": "0.1.0", + "version": "0.19.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "talemate_frontend", - "version": "0.1.0", + "version": "0.19.0", "dependencies": { "@mdi/font": "7.4.47", "core-js": "^3.8.3", diff --git a/talemate_frontend/package.json b/talemate_frontend/package.json index 5650cb0e..777c6858 100644 --- a/talemate_frontend/package.json +++ b/talemate_frontend/package.json @@ -1,6 +1,6 @@ { "name": "talemate_frontend", - "version": "0.1.0", + "version": "0.19.0", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/talemate_frontend/src/components/AIAgent.vue b/talemate_frontend/src/components/AIAgent.vue index d91eca9b..f5223bb4 100644 --- a/talemate_frontend/src/components/AIAgent.vue +++ b/talemate_frontend/src/components/AIAgent.vue @@ -3,11 +3,11 @@ - mdi-checkbox-blank-circle mdi-checkbox-blank-circle - mdi-checkbox-blank-circle + mdi-checkbox-blank-circle mdi-checkbox-blank-circle {{ agent.label }} diff --git a/talemate_frontend/src/components/AIClient.vue b/talemate_frontend/src/components/AIClient.vue index af94cc6d..8432db39 100644 --- a/talemate_frontend/src/components/AIClient.vue +++ b/talemate_frontend/src/components/AIClient.vue @@ -5,11 +5,11 @@ - mdi-checkbox-blank-circle - mdi-checkbox-blank-circle + mdi-checkbox-blank-circle mdi-checkbox-blank-circle mdi-checkbox-blank-circle {{ client.name }} @@ -84,6 +84,7 @@ export default { return { saveDelayTimeout: null, clientStatusCheck: null, + clientDeleted: false, state: { clients: [], dialog: false, @@ -191,6 +192,7 @@ export default { if (window.confirm('Are you sure you want to delete this client?')) { this.state.clients.splice(index, 1); this.$emit('clients-updated', this.state.clients); + this.clientDeleted = true; } }, assignClientToAllAgents(index) { @@ -212,6 +214,15 @@ export default { // Handle client_status message type if (data.type === 'client_status') { + + if(this.clientDeleted) { + + // If we have just deleted a client, we need to wait for the next client_status message + + this.clientDeleted = false; + return; + } + // Find the client with the given name const client = this.state.clients.find(client => client.name === data.name); diff --git a/talemate_frontend/src/components/AppConfig.vue b/talemate_frontend/src/components/AppConfig.vue index 59b9f876..4d76df28 100644 --- a/talemate_frontend/src/components/AppConfig.vue +++ b/talemate_frontend/src/components/AppConfig.vue @@ -199,7 +199,7 @@ - mdi-delete{{ value }} + mdi-close-box-outline{{ value }} @@ -226,7 +226,7 @@ Configuration - + diff --git a/talemate_frontend/src/components/CharacterCreator.vue b/talemate_frontend/src/components/CharacterCreator.vue index 3e6d7bab..c931ea52 100644 --- a/talemate_frontend/src/components/CharacterCreator.vue +++ b/talemate_frontend/src/components/CharacterCreator.vue @@ -75,7 +75,7 @@ - + Generate Reset @@ -88,7 +88,7 @@ - Generate @@ -105,7 +105,7 @@
- mdi-delete + mdi-close-box-outline {{ question }}
@@ -121,7 +121,7 @@ - Generate @@ -136,7 +136,7 @@
- mdi-delete + mdi-close-box-outline {{ example }}
@@ -144,7 +144,7 @@
- + Generate @@ -158,7 +158,7 @@ - + Add to world @@ -169,7 +169,7 @@ - + {{ notification_text }} diff --git a/talemate_frontend/src/components/CharacterImporter.vue b/talemate_frontend/src/components/CharacterImporter.vue index 1e25b761..ded2a047 100644 --- a/talemate_frontend/src/components/CharacterImporter.vue +++ b/talemate_frontend/src/components/CharacterImporter.vue @@ -16,7 +16,7 @@ Close Import - + diff --git a/talemate_frontend/src/components/CharacterSheet.vue b/talemate_frontend/src/components/CharacterSheet.vue index a6152adf..aee711db 100644 --- a/talemate_frontend/src/components/CharacterSheet.vue +++ b/talemate_frontend/src/components/CharacterSheet.vue @@ -3,7 +3,7 @@ Overview - Details + Attributes @@ -27,16 +27,14 @@
- - - {{ name }} - - -

+ + {{ name }} + +

{{ description }}

-
-
+ +
@@ -168,5 +166,11 @@ export default { } - + ``` \ No newline at end of file diff --git a/talemate_frontend/src/components/ContextualGenerate.vue b/talemate_frontend/src/components/ContextualGenerate.vue new file mode 100644 index 00000000..3236a0b5 --- /dev/null +++ b/talemate_frontend/src/components/ContextualGenerate.vue @@ -0,0 +1,156 @@ + + + \ No newline at end of file diff --git a/talemate_frontend/src/components/CreativeMenu.vue b/talemate_frontend/src/components/CreativeMenu.vue index ef8eb1f9..c599578f 100644 --- a/talemate_frontend/src/components/CreativeMenu.vue +++ b/talemate_frontend/src/components/CreativeMenu.vue @@ -15,7 +15,7 @@ @@ -53,7 +53,7 @@ diff --git a/talemate_frontend/src/components/DebugToolPromptLog.vue b/talemate_frontend/src/components/DebugToolPromptLog.vue index acb0eec3..b9cbf63c 100644 --- a/talemate_frontend/src/components/DebugToolPromptLog.vue +++ b/talemate_frontend/src/components/DebugToolPromptLog.vue @@ -13,16 +13,20 @@ #{{ prompt.num }} - {{ prompt.kind }} + + + {{ prompt.prompt_tokens }}mdi-arrow-down-bold + {{ prompt.response_tokens }}mdi-arrow-up-bold + {{ prompt.time }}smdi-clock +
- - {{ prompt.prompt_tokens }}mdi-arrow-down-bold - {{ prompt.response_tokens }}mdi-arrow-up-bold - {{ prompt.time }}smdi-clock + + {{ prompt.agent_name }} + {{ prompt.agent_action }}
@@ -66,14 +70,35 @@ export default { if(data.type === "prompt_sent") { // add to prompts array, and truncate if necessary (max 50) + + // get active agent (last in agent_stack if agent_stack is not empty) + let agent = null; + let agentName = null; + let agentAction = null; + + if(data.data.agent_stack.length > 0) { + agent = data.data.agent_stack[data.data.agent_stack.length - 1]; + // split by . to get agent name and action + let agentParts = agent.split('.'); + agentName = agentParts[0]; + agentAction = agentParts[1]; + } + this.prompts.unshift({ prompt: data.data.prompt, response: data.data.response, kind: data.data.kind, response_tokens: data.data.response_tokens, prompt_tokens: data.data.prompt_tokens, + agent_stack: data.data.agent_stack, + agent: agent, + agent_name: agentName, + agent_action: agentAction, + client_name: data.data.client_name, + client_type: data.data.client_type, time: parseInt(data.data.time), num: this.total++, + generation_parameters: data.data.generation_parameters, }) while(this.prompts.length > this.max_prompts) { @@ -83,7 +108,7 @@ export default { }, openPromptView(prompt) { - this.$refs.promptView.open(prompt); + this.$refs.promptView.open(prompt, this.prompts); } }, diff --git a/talemate_frontend/src/components/DebugToolPromptView.vue b/talemate_frontend/src/components/DebugToolPromptView.vue index 79a5a184..19eff0c1 100644 --- a/talemate_frontend/src/components/DebugToolPromptView.vue +++ b/talemate_frontend/src/components/DebugToolPromptView.vue @@ -1,28 +1,75 @@ @@ -34,17 +81,110 @@ export default { return { prompt: null, dialog: false, + details: false, + busy: false, + index: null, + prompts: [], } }, + computed: { + filteredParameters() { + // generation_parameters + // remove `prompt` + + let filtered = {}; + + for(let key in this.prompt.generation_parameters) { + if(key != 'prompt' && key != 'stream' && key != 'max_new_tokens') { + filtered[key] = this.prompt.generation_parameters[key]; + } + } + + return filtered; + }, + }, + inject: [ + "getWebsocket", + 'registerMessageHandler', + ], methods: { - open(prompt) { + + toggleDetailsLabel() { + return this.details ? 'Hide Details' : 'Show Details'; + }, + + toggleDetails() { + this.details = !this.details; + }, + + agentParts (agent) { + let parts = agent.split('.'); + return { + name: parts[0], + action: parts[1], + } + }, + + testChanges(){ + this.busy = true; + this.getWebsocket().send(JSON.stringify({ + type: "devtools", + action: "test_prompt", + prompt: this.prompt.prompt, + generation_parameters: this.prompt.generation_parameters, + kind: this.prompt.kind, + client_name: this.prompt.client_name, + })); + }, + + hasPreviousPrompt() { + return this.index < this.prompts.length - 1; + }, + + hasNextPrompt() { + return this.index > 0; + }, + + loadPreviousPrompt() { + if(this.index < this.prompts.length - 1) { + this.index++; + this.prompt = this.prompts[this.index]; + } + }, + + loadNextPrompt() { + if(this.index > 0) { + this.index--; + this.prompt = this.prompts[this.index]; + } + }, + + open(prompt, prompts) { this.prompt = prompt; + this.prompts = prompts; + + this.index = this.prompts.indexOf(prompt); + this.dialog = true; + this.busy = false; }, close() { this.dialog = false; - } - } + }, + handleMessage(data) { + if(data.type !== "devtools") { + return; + } + + if(data.action === "test_prompt_response" ) { + this.prompt.response = data.data.response; + this.busy = false; + } + }, + }, + created() { + this.registerMessageHandler(this.handleMessage); + }, } @@ -55,4 +195,11 @@ export default { white-space: pre-wrap; word-wrap: break-word; } + +.generation-parameters { + font-family: monospace; + font-size: 12px; + white-space: pre-wrap; + word-wrap: break-word; +} \ No newline at end of file diff --git a/talemate_frontend/src/components/DefaultCharacter.vue b/talemate_frontend/src/components/DefaultCharacter.vue index e65fb4a0..da3f91fd 100644 --- a/talemate_frontend/src/components/DefaultCharacter.vue +++ b/talemate_frontend/src/components/DefaultCharacter.vue @@ -25,7 +25,7 @@ Cancel - + Continue diff --git a/talemate_frontend/src/components/IntroRecentScenes.vue b/talemate_frontend/src/components/IntroRecentScenes.vue index 273d009f..2c15a622 100644 --- a/talemate_frontend/src/components/IntroRecentScenes.vue +++ b/talemate_frontend/src/components/IntroRecentScenes.vue @@ -1,11 +1,8 @@