diff --git a/Code/pipeline-example/.gitignore b/Code/pipeline-example/.gitignore new file mode 100644 index 0000000..96c32b5 --- /dev/null +++ b/Code/pipeline-example/.gitignore @@ -0,0 +1,224 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python,linux,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,macos + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python,linux,macos diff --git a/Code/pipeline-example/.python-version b/Code/pipeline-example/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/Code/pipeline-example/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/Code/pipeline-example/.vscode/settings.json b/Code/pipeline-example/.vscode/settings.json new file mode 100644 index 0000000..a281e49 --- /dev/null +++ b/Code/pipeline-example/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:system", + "python-envs.pythonProjects": [] +} diff --git a/Code/pipeline-example/README.md b/Code/pipeline-example/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Code/pipeline-example/docs/Python GenAI README.md b/Code/pipeline-example/docs/Python GenAI README.md new file mode 100644 index 0000000..16d142c --- /dev/null +++ b/Code/pipeline-example/docs/Python GenAI README.md @@ -0,0 +1,1353 @@ +# Google Gen AI SDK + +[![PyPI version](https://img.shields.io/pypi/v/google-genai.svg)](https://pypi.org/project/google-genai/) + +-------- +**Documentation:** https://googleapis.github.io/python-genai/ + +----- + +Google Gen AI Python SDK provides an interface for developers to integrate Google's generative models into their Python applications. It supports the [Gemini Developer API](https://ai.google.dev/gemini-api/docs) and [Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/overview) APIs. + +## Installation + +```sh +pip install google-genai +``` + +## Imports + +```python +from google import genai +from google.genai import types +``` + +## Create a client + +Please run one of the following code blocks to create a client for +different services ([Gemini Developer API](https://ai.google.dev/gemini-api/docs) or [Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/overview)). + +```python +# Only run this block for Gemini Developer API +client = genai.Client(api_key='GEMINI_API_KEY') +``` + +```python +# Only run this block for Vertex AI API +client = genai.Client( + vertexai=True, project='your-project-id', location='us-central1' +) +``` + +**(Optional) Using environment variables:** + +You can create a client by configuring the necessary environment variables. +Configuration setup instructions depends on whether you're using the Gemini +Developer API or the Gemini API in Vertex AI. + +**Gemini Developer API:** Set `GOOGLE_API_KEY` as shown below: + +```bash +export GOOGLE_API_KEY='your-api-key' +``` + +**Gemini API on Vertex AI:** Set `GOOGLE_GENAI_USE_VERTEXAI`, `GOOGLE_CLOUD_PROJECT` +and `GOOGLE_CLOUD_LOCATION`, as shown below: + +```bash +export GOOGLE_GENAI_USE_VERTEXAI=true +export GOOGLE_CLOUD_PROJECT='your-project-id' +export GOOGLE_CLOUD_LOCATION='us-central1' +``` + +```python +client = genai.Client() +``` + +### API Selection + +By default, the SDK uses the beta API endpoints provided by Google to support +preview features in the APIs. The stable API endpoints can be selected by +setting the API version to `v1`. + +To set the API version use `http_options`. For example, to set the API version +to `v1` for Vertex AI: + +```python +client = genai.Client( + vertexai=True, + project='your-project-id', + location='us-central1', + http_options=types.HttpOptions(api_version='v1') +) +``` + +To set the API version to `v1alpha` for the Gemini Developer API: + +```python +client = genai.Client( + api_key='GEMINI_API_KEY', + http_options=types.HttpOptions(api_version='v1alpha') +) +``` + +## Types + +Parameter types can be specified as either dictionaries(`TypedDict`) or +[Pydantic Models](https://pydantic.readthedocs.io/en/stable/model.html). +Pydantic model types are available in the `types` module. + +## Models + +The `client.models` modules exposes model inferencing and model getters. + +### Generate Content + +#### with text content + +```python +response = client.models.generate_content( + model='gemini-2.0-flash-001', contents='Why is the sky blue?' +) +print(response.text) +``` + +#### with uploaded file (Gemini Developer API only) +download the file in console. + +```sh +!wget -q https://storage.googleapis.com/generativeai-downloads/data/a11.txt +``` + +python code. + +```python +file = client.files.upload(file='a11.txt') +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents=['Could you summarize this file?', file] +) +print(response.text) +``` + +#### How to structure `contents` argument for `generate_content` +The SDK always converts the inputs to the `contents` argument into +`list[types.Content]`. +The following shows some common ways to provide your inputs. + +##### Provide a `list[types.Content]` +This is the canonical way to provide contents, SDK will not do any conversion. + +##### Provide a `types.Content` instance + +```python +contents = types.Content( + role='user', + parts=[types.Part.from_text(text='Why is the sky blue?')] +) +``` + +SDK converts this to + +```python +[ + types.Content( + role='user', + parts=[types.Part.from_text(text='Why is the sky blue?')] + ) +] +``` + +##### Provide a string + +```python +contents='Why is the sky blue?' +``` + +The SDK will assume this is a text part, and it converts this into the following: + +```python +[ + types.UserContent( + parts=[ + types.Part.from_text(text='Why is the sky blue?') + ] + ) +] +``` + +Where a `types.UserContent` is a subclass of `types.Content`, it sets the +`role` field to be `user`. + +##### Provide a list of string + +```python +contents=['Why is the sky blue?', 'Why is the cloud white?'] +``` + +The SDK assumes these are 2 text parts, it converts this into a single content, +like the following: + +```python +[ + types.UserContent( + parts=[ + types.Part.from_text(text='Why is the sky blue?'), + types.Part.from_text(text='Why is the cloud white?'), + ] + ) +] +``` + +Where a `types.UserContent` is a subclass of `types.Content`, the +`role` field in `types.UserContent` is fixed to be `user`. + +##### Provide a function call part + +```python +contents = types.Part.from_function_call( + name='get_weather_by_location', + args={'location': 'Boston'} +) +``` + +The SDK converts a function call part to a content with a `model` role: + +```python +[ + types.ModelContent( + parts=[ + types.Part.from_function_call( + name='get_weather_by_location', + args={'location': 'Boston'} + ) + ] + ) +] +``` + +Where a `types.ModelContent` is a subclass of `types.Content`, the +`role` field in `types.ModelContent` is fixed to be `model`. + +##### Provide a list of function call parts + +```python +contents = [ + types.Part.from_function_call( + name='get_weather_by_location', + args={'location': 'Boston'} + ), + types.Part.from_function_call( + name='get_weather_by_location', + args={'location': 'New York'} + ), +] +``` + +The SDK converts a list of function call parts to the a content with a `model` role: + +```python +[ + types.ModelContent( + parts=[ + types.Part.from_function_call( + name='get_weather_by_location', + args={'location': 'Boston'} + ), + types.Part.from_function_call( + name='get_weather_by_location', + args={'location': 'New York'} + ) + ] + ) +] +``` + +Where a `types.ModelContent` is a subclass of `types.Content`, the +`role` field in `types.ModelContent` is fixed to be `model`. + +##### Provide a non function call part + +```python +contents = types.Part.from_uri( + file_uri: 'gs://generativeai-downloads/images/scones.jpg', + mime_type: 'image/jpeg', +) +``` + +The SDK converts all non function call parts into a content with a `user` role. + +```python +[ + types.UserContent(parts=[ + types.Part.from_uri( + file_uri: 'gs://generativeai-downloads/images/scones.jpg', + mime_type: 'image/jpeg', + ) + ]) +] +``` + +##### Provide a list of non function call parts + +```python +contents = [ + types.Part.from_text('What is this image about?'), + types.Part.from_uri( + file_uri: 'gs://generativeai-downloads/images/scones.jpg', + mime_type: 'image/jpeg', + ) +] +``` + +The SDK will convert the list of parts into a content with a `user` role + +```python +[ + types.UserContent( + parts=[ + types.Part.from_text('What is this image about?'), + types.Part.from_uri( + file_uri: 'gs://generativeai-downloads/images/scones.jpg', + mime_type: 'image/jpeg', + ) + ] + ) +] +``` + +##### Mix types in contents + +You can also provide a list of `types.ContentUnion`. The SDK leaves items of +`types.Content` as is, it groups consecutive non function call parts into a +single `types.UserContent`, and it groups consecutive function call parts into +a single `types.ModelContent`. + +If you put a list within a list, the inner list can only contain +`types.PartUnion` items. The SDK will convert the inner list into a single +`types.UserContent`. + +### System Instructions and Other Configs + +The output of the model can be influenced by several optional settings +available in generate_content's config parameter. For example, the +variability and length of the output can be influenced by the temperature +and max_output_tokens respectively. + +```python +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='high', + config=types.GenerateContentConfig( + system_instruction='I say high, you say low', + max_output_tokens=3, + temperature=0.3, + ), +) +print(response.text) +``` + +### Typed Config + +All API methods support Pydantic types for parameters as well as +dictionaries. You can get the type from `google.genai.types`. + +```python +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents=types.Part.from_text(text='Why is the sky blue?'), + config=types.GenerateContentConfig( + temperature=0, + top_p=0.95, + top_k=20, + candidate_count=1, + seed=5, + max_output_tokens=100, + stop_sequences=['STOP!'], + presence_penalty=0.0, + frequency_penalty=0.0, + ), +) + +print(response.text) +``` + +### List Base Models + +To retrieve tuned models, see [list tuned models](#list-tuned-models). + +```python +for model in client.models.list(): + print(model) +``` + +```python +pager = client.models.list(config={'page_size': 10}) +print(pager.page_size) +print(pager[0]) +pager.next_page() +print(pager[0]) +``` + +#### Async + +```python +async for job in await client.aio.models.list(): + print(job) +``` + +```python +async_pager = await client.aio.models.list(config={'page_size': 10}) +print(async_pager.page_size) +print(async_pager[0]) +await async_pager.next_page() +print(async_pager[0]) +``` + +### Safety Settings + +```python +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='Say something bad.', + config=types.GenerateContentConfig( + safety_settings=[ + types.SafetySetting( + category='HARM_CATEGORY_HATE_SPEECH', + threshold='BLOCK_ONLY_HIGH', + ) + ] + ), +) +print(response.text) +``` + +### Function Calling + +#### Automatic Python function Support + +You can pass a Python function directly and it will be automatically +called and responded by default. + +```python +def get_current_weather(location: str) -> str: + """Returns the current weather. + + Args: + location: The city and state, e.g. San Francisco, CA + """ + return 'sunny' + + +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='What is the weather like in Boston?', + config=types.GenerateContentConfig(tools=[get_current_weather]), +) + +print(response.text) +``` +#### Disabling automatic function calling +If you pass in a python function as a tool directly, and do not want +automatic function calling, you can disable automatic function calling +as follows: + +```python +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='What is the weather like in Boston?', + config=types.GenerateContentConfig( + tools=[get_current_weather], + automatic_function_calling=types.AutomaticFunctionCallingConfig( + disable=True + ), + ), +) +``` + +With automatic function calling disabled, you will get a list of function call +parts in the response: + +```python +function_calls: Optional[List[types.FunctionCall]] = response.function_calls +``` + +#### Manually declare and invoke a function for function calling + +If you don't want to use the automatic function support, you can manually +declare the function and invoke it. + +The following example shows how to declare a function and pass it as a tool. +Then you will receive a function call part in the response. + +```python +function = types.FunctionDeclaration( + name='get_current_weather', + description='Get the current weather in a given location', + parameters=types.Schema( + type='OBJECT', + properties={ + 'location': types.Schema( + type='STRING', + description='The city and state, e.g. San Francisco, CA', + ), + }, + required=['location'], + ), +) + +tool = types.Tool(function_declarations=[function]) + +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='What is the weather like in Boston?', + config=types.GenerateContentConfig(tools=[tool]), +) + +print(response.function_calls[0]) +``` + +After you receive the function call part from the model, you can invoke the function +and get the function response. And then you can pass the function response to +the model. +The following example shows how to do it for a simple function invocation. + +```python +user_prompt_content = types.Content( + role='user', + parts=[types.Part.from_text(text='What is the weather like in Boston?')], +) +function_call_part = response.function_calls[0] +function_call_content = response.candidates[0].content + + +try: + function_result = get_current_weather( + **function_call_part.function_call.args + ) + function_response = {'result': function_result} +except ( + Exception +) as e: # instead of raising the exception, you can let the model handle it + function_response = {'error': str(e)} + + +function_response_part = types.Part.from_function_response( + name=function_call_part.name, + response=function_response, +) +function_response_content = types.Content( + role='tool', parts=[function_response_part] +) + +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents=[ + user_prompt_content, + function_call_content, + function_response_content, + ], + config=types.GenerateContentConfig( + tools=[tool], + ), +) + +print(response.text) +``` + +#### Function calling with `ANY` tools config mode + +If you configure function calling mode to be `ANY`, then the model will always +return function call parts. If you also pass a python function as a tool, by +default the SDK will perform automatic function calling until the remote calls exceed the +maximum remote call for automatic function calling (default to 10 times). + +If you'd like to disable automatic function calling in `ANY` mode: + +```python +def get_current_weather(location: str) -> str: + """Returns the current weather. + + Args: + location: The city and state, e.g. San Francisco, CA + """ + return "sunny" + +response = client.models.generate_content( + model="gemini-2.0-flash-001", + contents="What is the weather like in Boston?", + config=types.GenerateContentConfig( + tools=[get_current_weather], + automatic_function_calling=types.AutomaticFunctionCallingConfig( + disable=True + ), + tool_config=types.ToolConfig( + function_calling_config=types.FunctionCallingConfig(mode='ANY') + ), + ), +) +``` + +If you'd like to set `x` number of automatic function call turns, you can +configure the maximum remote calls to be `x + 1`. +Assuming you prefer `1` turn for automatic function calling. + +```python +def get_current_weather(location: str) -> str: + """Returns the current weather. + + Args: + location: The city and state, e.g. San Francisco, CA + """ + return "sunny" + +response = client.models.generate_content( + model="gemini-2.0-flash-001", + contents="What is the weather like in Boston?", + config=types.GenerateContentConfig( + tools=[get_current_weather], + automatic_function_calling=types.AutomaticFunctionCallingConfig( + maximum_remote_calls=2 + ), + tool_config=types.ToolConfig( + function_calling_config=types.FunctionCallingConfig(mode='ANY') + ), + ), +) +``` +### JSON Response Schema + +However you define your schema, don't duplicate it in your input prompt, +including by giving examples of expected JSON output. If you do, the generated +output might be lower in quality. + +#### Pydantic Model Schema support + +Schemas can be provided as Pydantic Models. + +```python +from pydantic import BaseModel + + +class CountryInfo(BaseModel): + name: str + population: int + capital: str + continent: str + gdp: int + official_language: str + total_area_sq_mi: int + + +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='Give me information for the United States.', + config=types.GenerateContentConfig( + response_mime_type='application/json', + response_schema=CountryInfo, + ), +) +print(response.text) +``` + +```python +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='Give me information for the United States.', + config=types.GenerateContentConfig( + response_mime_type='application/json', + response_schema={ + 'required': [ + 'name', + 'population', + 'capital', + 'continent', + 'gdp', + 'official_language', + 'total_area_sq_mi', + ], + 'properties': { + 'name': {'type': 'STRING'}, + 'population': {'type': 'INTEGER'}, + 'capital': {'type': 'STRING'}, + 'continent': {'type': 'STRING'}, + 'gdp': {'type': 'INTEGER'}, + 'official_language': {'type': 'STRING'}, + 'total_area_sq_mi': {'type': 'INTEGER'}, + }, + 'type': 'OBJECT', + }, + ), +) +print(response.text) +``` + +### Enum Response Schema + +#### Text Response + +You can set response_mime_type to 'text/x.enum' to return one of those enum +values as the response. + +```python +class InstrumentEnum(Enum): + PERCUSSION = 'Percussion' + STRING = 'String' + WOODWIND = 'Woodwind' + BRASS = 'Brass' + KEYBOARD = 'Keyboard' + +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='What instrument plays multiple notes at once?', + config={ + 'response_mime_type': 'text/x.enum', + 'response_schema': InstrumentEnum, + }, + ) +print(response.text) +``` + +#### JSON Response + +You can also set response_mime_type to 'application/json', the response will be identical but in quotes. + +```python +from enum import Enum + +class InstrumentEnum(Enum): + PERCUSSION = 'Percussion' + STRING = 'String' + WOODWIND = 'Woodwind' + BRASS = 'Brass' + KEYBOARD = 'Keyboard' + +response = client.models.generate_content( + model='gemini-2.0-flash-001', + contents='What instrument plays multiple notes at once?', + config={ + 'response_mime_type': 'application/json', + 'response_schema': InstrumentEnum, + }, + ) +print(response.text) +``` + +### Streaming + +#### Streaming for text content + +```python +for chunk in client.models.generate_content_stream( + model='gemini-2.0-flash-001', contents='Tell me a story in 300 words.' +): + print(chunk.text, end='') +``` + +#### Streaming for image content + +If your image is stored in [Google Cloud Storage](https://cloud.google.com/storage), +you can use the `from_uri` class method to create a `Part` object. + +```python +for chunk in client.models.generate_content_stream( + model='gemini-2.0-flash-001', + contents=[ + 'What is this image about?', + types.Part.from_uri( + file_uri='gs://generativeai-downloads/images/scones.jpg', + mime_type='image/jpeg', + ), + ], +): + print(chunk.text, end='') +``` + +If your image is stored in your local file system, you can read it in as bytes +data and use the `from_bytes` class method to create a `Part` object. + +```python +YOUR_IMAGE_PATH = 'your_image_path' +YOUR_IMAGE_MIME_TYPE = 'your_image_mime_type' +with open(YOUR_IMAGE_PATH, 'rb') as f: + image_bytes = f.read() + +for chunk in client.models.generate_content_stream( + model='gemini-2.0-flash-001', + contents=[ + 'What is this image about?', + types.Part.from_bytes(data=image_bytes, mime_type=YOUR_IMAGE_MIME_TYPE), + ], +): + print(chunk.text, end='') +``` + +### Async + +`client.aio` exposes all the analogous [`async` methods](https://docs.python.org/3/library/asyncio.html) +that are available on `client` + +For example, `client.aio.models.generate_content` is the `async` version +of `client.models.generate_content` + +```python +response = await client.aio.models.generate_content( + model='gemini-2.0-flash-001', contents='Tell me a story in 300 words.' +) + +print(response.text) +``` + +### Streaming + +```python +async for chunk in await client.aio.models.generate_content_stream( + model='gemini-2.0-flash-001', contents='Tell me a story in 300 words.' +): + print(chunk.text, end='') +``` + +### Count Tokens and Compute Tokens + +```python +response = client.models.count_tokens( + model='gemini-2.0-flash-001', + contents='why is the sky blue?', +) +print(response) +``` + +#### Compute Tokens + +Compute tokens is only supported in Vertex AI. + +```python +response = client.models.compute_tokens( + model='gemini-2.0-flash-001', + contents='why is the sky blue?', +) +print(response) +``` + +##### Async + +```python +response = await client.aio.models.count_tokens( + model='gemini-2.0-flash-001', + contents='why is the sky blue?', +) +print(response) +``` + +### Embed Content + +```python +response = client.models.embed_content( + model='text-embedding-004', + contents='why is the sky blue?', +) +print(response) +``` + +```python +# multiple contents with config +response = client.models.embed_content( + model='text-embedding-004', + contents=['why is the sky blue?', 'What is your age?'], + config=types.EmbedContentConfig(output_dimensionality=10), +) + +print(response) +``` + +### Imagen + +#### Generate Images + +Support for generate images in Gemini Developer API is behind an allowlist + +```python +# Generate Image +response1 = client.models.generate_images( + model='imagen-3.0-generate-002', + prompt='An umbrella in the foreground, and a rainy night sky in the background', + config=types.GenerateImagesConfig( + number_of_images=1, + include_rai_reason=True, + output_mime_type='image/jpeg', + ), +) +response1.generated_images[0].image.show() +``` + +#### Upscale Image + +Upscale image is only supported in Vertex AI. + +```python +# Upscale the generated image from above +response2 = client.models.upscale_image( + model='imagen-3.0-generate-001', + image=response1.generated_images[0].image, + upscale_factor='x2', + config=types.UpscaleImageConfig( + include_rai_reason=True, + output_mime_type='image/jpeg', + ), +) +response2.generated_images[0].image.show() +``` + +#### Edit Image + +Edit image uses a separate model from generate and upscale. + +Edit image is only supported in Vertex AI. + +```python +# Edit the generated image from above +from google.genai.types import RawReferenceImage, MaskReferenceImage + +raw_ref_image = RawReferenceImage( + reference_id=1, + reference_image=response1.generated_images[0].image, +) + +# Model computes a mask of the background +mask_ref_image = MaskReferenceImage( + reference_id=2, + config=types.MaskReferenceConfig( + mask_mode='MASK_MODE_BACKGROUND', + mask_dilation=0, + ), +) + +response3 = client.models.edit_image( + model='imagen-3.0-capability-001', + prompt='Sunlight and clear sky', + reference_images=[raw_ref_image, mask_ref_image], + config=types.EditImageConfig( + edit_mode='EDIT_MODE_INPAINT_INSERTION', + number_of_images=1, + include_rai_reason=True, + output_mime_type='image/jpeg', + ), +) +response3.generated_images[0].image.show() +``` + +### Veo + +#### Generate Videos + +Support for generate videos in Vertex and Gemini Developer API is behind an allowlist + +```python +# Create operation +operation = client.models.generate_videos( + model='veo-2.0-generate-001', + prompt='A neon hologram of a cat driving at top speed', + config=types.GenerateVideosConfig( + number_of_videos=1, + fps=24, + duration_seconds=5, + enhance_prompt=True, + ), +) + +# Poll operation +while not operation.done: + time.sleep(20) + operation = client.operations.get(operation) + +video = operation.result.generated_videos[0].video +video.show() +``` + +## Chats + +Create a chat session to start a multi-turn conversations with the model. + +### Send Message + +```python +chat = client.chats.create(model='gemini-2.0-flash-001') +response = chat.send_message('tell me a story') +print(response.text) +``` + +### Streaming + +```python +chat = client.chats.create(model='gemini-2.0-flash-001') +for chunk in chat.send_message_stream('tell me a story'): + print(chunk.text) +``` + +### Async + +```python +chat = client.aio.chats.create(model='gemini-2.0-flash-001') +response = await chat.send_message('tell me a story') +print(response.text) +``` + +### Async Streaming + +```python +chat = client.aio.chats.create(model='gemini-2.0-flash-001') +async for chunk in await chat.send_message_stream('tell me a story'): + print(chunk.text) +``` + +## Files + +Files are only supported in Gemini Developer API. + +```cmd +!gsutil cp gs://cloud-samples-data/generative-ai/pdf/2312.11805v3.pdf . +!gsutil cp gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf . +``` + +### Upload + +```python +file1 = client.files.upload(file='2312.11805v3.pdf') +file2 = client.files.upload(file='2403.05530.pdf') + +print(file1) +print(file2) +``` + +### Get + +```python +file1 = client.files.upload(file='2312.11805v3.pdf') +file_info = client.files.get(name=file1.name) +``` + +### Delete + +```python +file3 = client.files.upload(file='2312.11805v3.pdf') + +client.files.delete(name=file3.name) +``` + +## Caches + +`client.caches` contains the control plane APIs for cached content + +### Create + +```python +if client.vertexai: + file_uris = [ + 'gs://cloud-samples-data/generative-ai/pdf/2312.11805v3.pdf', + 'gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf', + ] +else: + file_uris = [file1.uri, file2.uri] + +cached_content = client.caches.create( + model='gemini-1.5-pro-002', + config=types.CreateCachedContentConfig( + contents=[ + types.Content( + role='user', + parts=[ + types.Part.from_uri( + file_uri=file_uris[0], mime_type='application/pdf' + ), + types.Part.from_uri( + file_uri=file_uris[1], + mime_type='application/pdf', + ), + ], + ) + ], + system_instruction='What is the sum of the two pdfs?', + display_name='test cache', + ttl='3600s', + ), +) +``` + +### Get + +```python +cached_content = client.caches.get(name=cached_content.name) +``` + +### Generate Content with Caches + +```python +response = client.models.generate_content( + model='gemini-1.5-pro-002', + contents='Summarize the pdfs', + config=types.GenerateContentConfig( + cached_content=cached_content.name, + ), +) +print(response.text) +``` + +## Tunings + +`client.tunings` contains tuning job APIs and supports supervised fine +tuning through `tune`. + +### Tune + +- Vertex AI supports tuning from GCS source +- Gemini Developer API supports tuning from inline examples + +```python +if client.vertexai: + model = 'gemini-1.5-pro-002' + training_dataset = types.TuningDataset( + gcs_uri='gs://cloud-samples-data/ai-platform/generative_ai/gemini-1_5/text/sft_train_data.jsonl', + ) +else: + model = 'models/gemini-1.0-pro-001' + training_dataset = types.TuningDataset( + examples=[ + types.TuningExample( + text_input=f'Input text {i}', + output=f'Output text {i}', + ) + for i in range(5) + ], + ) +``` + +```python +tuning_job = client.tunings.tune( + base_model=model, + training_dataset=training_dataset, + config=types.CreateTuningJobConfig( + epoch_count=1, tuned_model_display_name='test_dataset_examples model' + ), +) +print(tuning_job) +``` + +### Get Tuning Job + +```python +tuning_job = client.tunings.get(name=tuning_job.name) +print(tuning_job) +``` + +```python +import time + +running_states = set( + [ + 'JOB_STATE_PENDING', + 'JOB_STATE_RUNNING', + ] +) + +while tuning_job.state in running_states: + print(tuning_job.state) + tuning_job = client.tunings.get(name=tuning_job.name) + time.sleep(10) +``` + +#### Use Tuned Model + +```python +response = client.models.generate_content( + model=tuning_job.tuned_model.endpoint, + contents='why is the sky blue?', +) + +print(response.text) +``` + +### Get Tuned Model + +```python +tuned_model = client.models.get(model=tuning_job.tuned_model.model) +print(tuned_model) +``` + +### List Tuned Models + +To retrieve base models, see [list base models](#list-base-models). + +```python +for model in client.models.list(config={'page_size': 10, 'query_base': False}): + print(model) +``` + +```python +pager = client.models.list(config={'page_size': 10, 'query_base': False}) +print(pager.page_size) +print(pager[0]) +pager.next_page() +print(pager[0]) +``` + +#### Async + +```python +async for job in await client.aio.models.list(config={'page_size': 10, 'query_base': False}): + print(job) +``` + +```python +async_pager = await client.aio.models.list(config={'page_size': 10, 'query_base': False}) +print(async_pager.page_size) +print(async_pager[0]) +await async_pager.next_page() +print(async_pager[0]) +``` + +### Update Tuned Model + +```python +model = pager[0] + +model = client.models.update( + model=model.name, + config=types.UpdateModelConfig( + display_name='my tuned model', description='my tuned model description' + ), +) + +print(model) +``` + + +### List Tuning Jobs + +```python +for job in client.tunings.list(config={'page_size': 10}): + print(job) +``` + +```python +pager = client.tunings.list(config={'page_size': 10}) +print(pager.page_size) +print(pager[0]) +pager.next_page() +print(pager[0]) +``` + +#### Async + +```python +async for job in await client.aio.tunings.list(config={'page_size': 10}): + print(job) +``` + +```python +async_pager = await client.aio.tunings.list(config={'page_size': 10}) +print(async_pager.page_size) +print(async_pager[0]) +await async_pager.next_page() +print(async_pager[0]) +``` + +## Batch Prediction + +Only supported in Vertex AI. + +### Create + +```python +# Specify model and source file only, destination and job display name will be auto-populated +job = client.batches.create( + model='gemini-1.5-flash-002', + src='bq://my-project.my-dataset.my-table', +) + +job +``` + +```python +# Get a job by name +job = client.batches.get(name=job.name) + +job.state +``` + +```python +completed_states = set( + [ + 'JOB_STATE_SUCCEEDED', + 'JOB_STATE_FAILED', + 'JOB_STATE_CANCELLED', + 'JOB_STATE_PAUSED', + ] +) + +while job.state not in completed_states: + print(job.state) + job = client.batches.get(name=job.name) + time.sleep(30) + +job +``` + +### List + +```python +for job in client.batches.list(config=types.ListBatchJobsConfig(page_size=10)): + print(job) +``` + +```python +pager = client.batches.list(config=types.ListBatchJobsConfig(page_size=10)) +print(pager.page_size) +print(pager[0]) +pager.next_page() +print(pager[0]) +``` + +#### Async + +```python +async for job in await client.aio.batches.list( + config=types.ListBatchJobsConfig(page_size=10) +): + print(job) +``` + +```python +async_pager = await client.aio.batches.list( + config=types.ListBatchJobsConfig(page_size=10) +) +print(async_pager.page_size) +print(async_pager[0]) +await async_pager.next_page() +print(async_pager[0]) +``` + +### Delete + +```python +# Delete the job resource +delete_job = client.batches.delete(name=job.name) + +delete_job +``` + +## Error Handling + +To handle errors raised by the model service, the SDK provides this [APIError](https://github.com/googleapis/python-genai/blob/main/google/genai/errors.py) class. + +```python +from google.genai import errors + +try: + client.models.generate_content( + model="invalid-model-name", + contents="What is your name?", + ) +except errors.APIError as e: + print(e.code) # 404 + print(e.message) +``` diff --git a/Code/pipeline-example/docs/apidoc.md b/Code/pipeline-example/docs/apidoc.md new file mode 100644 index 0000000..04ee299 --- /dev/null +++ b/Code/pipeline-example/docs/apidoc.md @@ -0,0 +1,1636 @@ +# API + +## Endpoints + +- [Generate a completion](#generate-a-completion) +- [Generate a chat completion](#generate-a-chat-completion) +- [Create a Model](#create-a-model) +- [List Local Models](#list-local-models) +- [Show Model Information](#show-model-information) +- [Copy a Model](#copy-a-model) +- [Delete a Model](#delete-a-model) +- [Pull a Model](#pull-a-model) +- [Push a Model](#push-a-model) +- [Generate Embeddings](#generate-embeddings) +- [List Running Models](#list-running-models) +- [Version](#version) + +## Conventions + +### Model names + +Model names follow a `model:tag` format, where `model` can have an optional namespace such as `example/model`. Some examples are `orca-mini:3b-q4_1` and `llama3:70b`. The tag is optional and, if not provided, will default to `latest`. The tag is used to identify a specific version. + +### Durations + +All durations are returned in nanoseconds. + +### Streaming responses + +Certain endpoints stream responses as JSON objects. Streaming can be disabled by providing `{"stream": false}` for these endpoints. + +## Generate a completion + +``` +POST /api/generate +``` + +Generate a response for a given prompt with a provided model. This is a streaming endpoint, so there will be a series of responses. The final response object will include statistics and additional data from the request. + +### Parameters + +- `model`: (required) the [model name](#model-names) +- `prompt`: the prompt to generate a response for +- `suffix`: the text after the model response +- `images`: (optional) a list of base64-encoded images (for multimodal models such as `llava`) + +Advanced parameters (optional): + +- `format`: the format to return a response in. Format can be `json` or a JSON schema +- `options`: additional model parameters listed in the documentation for the [Modelfile](./modelfile.md#valid-parameters-and-values) such as `temperature` +- `system`: system message to (overrides what is defined in the `Modelfile`) +- `template`: the prompt template to use (overrides what is defined in the `Modelfile`) +- `stream`: if `false` the response will be returned as a single response object, rather than a stream of objects +- `raw`: if `true` no formatting will be applied to the prompt. You may choose to use the `raw` parameter if you are specifying a full templated prompt in your request to the API +- `keep_alive`: controls how long the model will stay loaded into memory following the request (default: `5m`) +- `context` (deprecated): the context parameter returned from a previous request to `/generate`, this can be used to keep a short conversational memory + +#### Structured outputs + +Structured outputs are supported by providing a JSON schema in the `format` parameter. The model will generate a response that matches the schema. See the [structured outputs](#request-structured-outputs) example below. + +#### JSON mode + +Enable JSON mode by setting the `format` parameter to `json`. This will structure the response as a valid JSON object. See the JSON mode [example](#request-json-mode) below. + +> [!IMPORTANT] +> It's important to instruct the model to use JSON in the `prompt`. Otherwise, the model may generate large amounts whitespace. + +### Examples + +#### Generate request (Streaming) + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llama3.2", + "prompt": "Why is the sky blue?" +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T08:52:19.385406455-07:00", + "response": "The", + "done": false +} +``` + +The final response in the stream also includes additional data about the generation: + +- `total_duration`: time spent generating the response +- `load_duration`: time spent in nanoseconds loading the model +- `prompt_eval_count`: number of tokens in the prompt +- `prompt_eval_duration`: time spent in nanoseconds evaluating the prompt +- `eval_count`: number of tokens in the response +- `eval_duration`: time in nanoseconds spent generating the response +- `context`: an encoding of the conversation used in this response, this can be sent in the next request to keep a conversational memory +- `response`: empty if the response was streamed, if not streamed, this will contain the full response + +To calculate how fast the response is generated in tokens per second (token/s), divide `eval_count` / `eval_duration` * `10^9`. + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T19:22:45.499127Z", + "response": "", + "done": true, + "context": [1, 2, 3], + "total_duration": 10706818083, + "load_duration": 6338219291, + "prompt_eval_count": 26, + "prompt_eval_duration": 130079000, + "eval_count": 259, + "eval_duration": 4232710000 +} +``` + +#### Request (No streaming) + +##### Request + +A response can be received in one reply when streaming is off. + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llama3.2", + "prompt": "Why is the sky blue?", + "stream": false +}' +``` + +##### Response + +If `stream` is set to `false`, the response will be a single JSON object: + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T19:22:45.499127Z", + "response": "The sky is blue because it is the color of the sky.", + "done": true, + "context": [1, 2, 3], + "total_duration": 5043500667, + "load_duration": 5025959, + "prompt_eval_count": 26, + "prompt_eval_duration": 325953000, + "eval_count": 290, + "eval_duration": 4709213000 +} +``` + +#### Request (with suffix) + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "codellama:code", + "prompt": "def compute_gcd(a, b):", + "suffix": " return result", + "options": { + "temperature": 0 + }, + "stream": false +}' +``` + +##### Response + +```json +{ + "model": "codellama:code", + "created_at": "2024-07-22T20:47:51.147561Z", + "response": "\n if a == 0:\n return b\n else:\n return compute_gcd(b % a, a)\n\ndef compute_lcm(a, b):\n result = (a * b) / compute_gcd(a, b)\n", + "done": true, + "done_reason": "stop", + "context": [...], + "total_duration": 1162761250, + "load_duration": 6683708, + "prompt_eval_count": 17, + "prompt_eval_duration": 201222000, + "eval_count": 63, + "eval_duration": 953997000 +} +``` + +#### Request (Structured outputs) + +##### Request + +```shell +curl -X POST http://localhost:11434/api/generate -H "Content-Type: application/json" -d '{ + "model": "llama3.1:8b", + "prompt": "Ollama is 22 years old and is busy saving the world. Respond using JSON", + "stream": false, + "format": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "available": { + "type": "boolean" + } + }, + "required": [ + "age", + "available" + ] + } +}' +``` + +##### Response + +```json +{ + "model": "llama3.1:8b", + "created_at": "2024-12-06T00:48:09.983619Z", + "response": "{\n \"age\": 22,\n \"available\": true\n}", + "done": true, + "done_reason": "stop", + "context": [1, 2, 3], + "total_duration": 1075509083, + "load_duration": 567678166, + "prompt_eval_count": 28, + "prompt_eval_duration": 236000000, + "eval_count": 16, + "eval_duration": 269000000 +} +``` + +#### Request (JSON mode) + +> [!IMPORTANT] +> When `format` is set to `json`, the output will always be a well-formed JSON object. It's important to also instruct the model to respond in JSON. + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llama3.2", + "prompt": "What color is the sky at different times of the day? Respond using JSON", + "format": "json", + "stream": false +}' +``` + +##### Response + +```json +{ + "model": "llama3.2", + "created_at": "2023-11-09T21:07:55.186497Z", + "response": "{\n\"morning\": {\n\"color\": \"blue\"\n},\n\"noon\": {\n\"color\": \"blue-gray\"\n},\n\"afternoon\": {\n\"color\": \"warm gray\"\n},\n\"evening\": {\n\"color\": \"orange\"\n}\n}\n", + "done": true, + "context": [1, 2, 3], + "total_duration": 4648158584, + "load_duration": 4071084, + "prompt_eval_count": 36, + "prompt_eval_duration": 439038000, + "eval_count": 180, + "eval_duration": 4196918000 +} +``` + +The value of `response` will be a string containing JSON similar to: + +```json +{ + "morning": { + "color": "blue" + }, + "noon": { + "color": "blue-gray" + }, + "afternoon": { + "color": "warm gray" + }, + "evening": { + "color": "orange" + } +} +``` + +#### Request (with images) + +To submit images to multimodal models such as `llava` or `bakllava`, provide a list of base64-encoded `images`: + +#### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llava", + "prompt":"What is in this picture?", + "stream": false, + "images": ["iVBORw0KGgoAAAANSUhEUgAAAG0AAABmCAYAAADBPx+VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAA3VSURBVHgB7Z27r0zdG8fX743i1bi1ikMoFMQloXRpKFFIqI7LH4BEQ+NWIkjQuSWCRIEoULk0gsK1kCBI0IhrQVT7tz/7zZo888yz1r7MnDl7z5xvsjkzs2fP3uu71nNfa7lkAsm7d++Sffv2JbNmzUqcc8m0adOSzZs3Z+/XES4ZckAWJEGWPiCxjsQNLWmQsWjRIpMseaxcuTKpG/7HP27I8P79e7dq1ars/yL4/v27S0ejqwv+cUOGEGGpKHR37tzJCEpHV9tnT58+dXXCJDdECBE2Ojrqjh071hpNECjx4cMHVycM1Uhbv359B2F79+51586daxN/+pyRkRFXKyRDAqxEp4yMlDDzXG1NPnnyJKkThoK0VFd1ELZu3TrzXKxKfW7dMBQ6bcuWLW2v0VlHjx41z717927ba22U9APcw7Nnz1oGEPeL3m3p2mTAYYnFmMOMXybPPXv2bNIPpFZr1NHn4HMw0KRBjg9NuRw95s8PEcz/6DZELQd/09C9QGq5RsmSRybqkwHGjh07OsJSsYYm3ijPpyHzoiacg35MLdDSIS/O1yM778jOTwYUkKNHWUzUWaOsylE00MyI0fcnOwIdjvtNdW/HZwNLGg+sR1kMepSNJXmIwxBZiG8tDTpEZzKg0GItNsosY8USkxDhD0Rinuiko2gfL/RbiD2LZAjU9zKQJj8RDR0vJBR1/Phx9+PHj9Z7REF4nTZkxzX4LCXHrV271qXkBAPGfP/atWvu/PnzHe4C97F48eIsRLZ9+3a3f/9+87dwP1JxaF7/3r17ba+5l4EcaVo0lj3SBq5kGTJSQmLWMjgYNei2GPT1MuMqGTDEFHzeQSP2wi/jGnkmPJ/nhccs44jvDAxpVcxnq0F6eT8h4ni/iIWpR5lPyA6ETkNXoSukvpJAD3AsXLiwpZs49+fPn5ke4j10TqYvegSfn0OnafC+Tv9ooA/JPkgQysqQNBzagXY55nO/oa1F7qvIPWkRL12WRpMWUvpVDYmxAPehxWSe8ZEXL20sadYIozfmNch4QJPAfeJgW3rNsnzphBKNJM2KKODo1rVOMRYik5ETy3ix4qWNI81qAAirizgMIc+yhTytx0JWZuNI03qsrgWlGtwjoS9XwgUhWGyhUaRZZQNNIEwCiXD16tXcAHUs79co0vSD8rrJCIW98pzvxpAWyyo3HYwqS0+H0BjStClcZJT5coMm6D2LOF8TolGJtK9fvyZpyiC5ePFi9nc/oJU4eiEP0jVoAnHa9wyJycITMP78+eMeP37sXrx44d6+fdt6f82aNdkx1pg9e3Zb5W+RSRE+n+VjksQWifvVaTKFhn5O8my63K8Qabdv33b379/PiAP//vuvW7BggZszZ072/+TJk91YgkafPn166zXB1rQHFvouAWHq9z3SEevSUerqCn2/dDCeta2jxYbr69evk4MHDyY7d+7MjhMnTiTPnz9Pfv/+nfQT2ggpO2dMF8cghuoM7Ygj5iWCqRlGFml0QC/ftGmTmzt3rmsaKDsgBSPh0/8yPeLLBihLkOKJc0jp8H8vUzcxIA1k6QJ/c78tWEyj5P3o4u9+jywNPdJi5rAH9x0KHcl4Hg570eQp3+vHXGyrmEeigzQsQsjavXt38ujRo44LQuDDhw+TW7duRS1HGgMxhNXHgflaNTOsHyKvHK5Ijo2jbFjJBQK9YwFd6RVMzfgRBmEfP37suBBm/p49e1qjEP2mwTViNRo0VJWH1deMXcNK08uUjVUu7s/zRaL+oLNxz1bpANco4npUgX4G2eFbpDFyQoQxojBCpEGSytmOH8qrH5Q9vuzD6ofQylkCUmh8DBAr+q8JCyVNtWQIidKQE9wNtLSQnS4jDSsxNHogzFuQBw4cyM61UKVsjfr3ooBkPSqqQHesUPWVtzi9/vQi1T+rJj7WiTz4Pt/l3LxUkr5P2VYZaZ4URpsE+st/dujQoaBBYokbrz/8TJNQYLSonrPS9kUaSkPeZyj1AWSj+d+VBoy1pIWVNed8P0Ll/ee5HdGRhrHhR5GGN0r4LGZBaj8oFDJitBTJzIZgFcmU0Y8ytWMZMzJOaXUSrUs5RxKnrxmbb5YXO9VGUhtpXldhEUogFr3IzIsvlpmdosVcGVGXFWp2oU9kLFL3dEkSz6NHEY1sjSRdIuDFWEhd8KxFqsRi1uM/nz9/zpxnwlESONdg6dKlbsaMGS4EHFHtjFIDHwKOo46l4TxSuxgDzi+rE2jg+BaFruOX4HXa0Nnf1lwAPufZeF8/r6zD97WK2qFnGjBxTw5qNGPxT+5T/r7/7RawFC3j4vTp09koCxkeHjqbHJqArmH5UrFKKksnxrK7FuRIs8STfBZv+luugXZ2pR/pP9Ois4z+TiMzUUkUjD0iEi1fzX8GmXyuxUBRcaUfykV0YZnlJGKQpOiGB76x5GeWkWWJc3mOrK6S7xdND+W5N6XyaRgtWJFe13GkaZnKOsYqGdOVVVbGupsyA/l7emTLHi7vwTdirNEt0qxnzAvBFcnQF16xh/TMpUuXHDowhlA9vQVraQhkudRdzOnK+04ZSP3DUhVSP61YsaLtd/ks7ZgtPcXqPqEafHkdqa84X6aCeL7YWlv6edGFHb+ZFICPlljHhg0bKuk0CSvVznWsotRu433alNdFrqG45ejoaPCaUkWERpLXjzFL2Rpllp7PJU2a/v7Ab8N05/9t27Z16KUqoFGsxnI9EosS2niSYg9SpU6B4JgTrvVW1flt1sT+0ADIJU2maXzcUTraGCRaL1Wp9rUMk16PMom8QhruxzvZIegJjFU7LLCePfS8uaQdPny4jTTL0dbee5mYokQsXTIWNY46kuMbnt8Kmec+LGWtOVIl9cT1rCB0V8WqkjAsRwta93TbwNYoGKsUSChN44lgBNCoHLHzquYKrU6qZ8lolCIN0Rh6cP0Q3U6I6IXILYOQI513hJaSKAorFpuHXJNfVlpRtmYBk1Su1obZr5dnKAO+L10Hrj3WZW+E3qh6IszE37F6EB+68mGpvKm4eb9bFrlzrok7fvr0Kfv727dvWRmdVTJHw0qiiCUSZ6wCK+7XL/AcsgNyL74DQQ730sv78Su7+t/A36MdY0sW5o40ahslXr58aZ5HtZB8GH64m9EmMZ7FpYw4T6QnrZfgenrhFxaSiSGXtPnz57e9TkNZLvTjeqhr734CNtrK41L40sUQckmj1lGKQ0rC37x544r8eNXRpnVE3ZZY7zXo8NomiO0ZUCj2uHz58rbXoZ6gc0uA+F6ZeKS/jhRDUq8MKrTho9fEkihMmhxtBI1DxKFY9XLpVcSkfoi8JGnToZO5sU5aiDQIW716ddt7ZLYtMQlhECdBGXZZMWldY5BHm5xgAroWj4C0hbYkSc/jBmggIrXJWlZM6pSETsEPGqZOndr2uuuR5rF169a2HoHPdurUKZM4CO1WTPqaDaAd+GFGKdIQkxAn9RuEWcTRyN2KSUgiSgF5aWzPTeA/lN5rZubMmR2bE4SIC4nJoltgAV/dVefZm72AtctUCJU2CMJ327hxY9t7EHbkyJFseq+EJSY16RPo3Dkq1kkr7+q0bNmyDuLQcZBEPYmHVdOBiJyIlrRDq41YPWfXOxUysi5fvtyaj+2BpcnsUV/oSoEMOk2CQGlr4ckhBwaetBhjCwH0ZHtJROPJkyc7UjcYLDjmrH7ADTEBXFfOYmB0k9oYBOjJ8b4aOYSe7QkKcYhFlq3QYLQhSidNmtS2RATwy8YOM3EQJsUjKiaWZ+vZToUQgzhkHXudb/PW5YMHD9yZM2faPsMwoc7RciYJXbGuBqJ1UIGKKLv915jsvgtJxCZDubdXr165mzdvtr1Hz5LONA8jrUwKPqsmVesKa49S3Q4WxmRPUEYdTjgiUcfUwLx589ySJUva3oMkP6IYddq6HMS4o55xBJBUeRjzfa4Zdeg56QZ43LhxoyPo7Lf1kNt7oO8wWAbNwaYjIv5lhyS7kRf96dvm5Jah8vfvX3flyhX35cuX6HfzFHOToS1H4BenCaHvO8pr8iDuwoUL7tevX+b5ZdbBair0xkFIlFDlW4ZknEClsp/TzXyAKVOmmHWFVSbDNw1l1+4f90U6IY/q4V27dpnE9bJ+v87QEydjqx/UamVVPRG+mwkNTYN+9tjkwzEx+atCm/X9WvWtDtAb68Wy9LXa1UmvCDDIpPkyOQ5ZwSzJ4jMrvFcr0rSjOUh+GcT4LSg5ugkW1Io0/SCDQBojh0hPlaJdah+tkVYrnTZowP8iq1F1TgMBBauufyB33x1v+NWFYmT5KmppgHC+NkAgbmRkpD3yn9QIseXymoTQFGQmIOKTxiZIWpvAatenVqRVXf2nTrAWMsPnKrMZHz6bJq5jvce6QK8J1cQNgKxlJapMPdZSR64/UivS9NztpkVEdKcrs5alhhWP9NeqlfWopzhZScI6QxseegZRGeg5a8C3Re1Mfl1ScP36ddcUaMuv24iOJtz7sbUjTS4qBvKmstYJoUauiuD3k5qhyr7QdUHMeCgLa1Ear9NquemdXgmum4fvJ6w1lqsuDhNrg1qSpleJK7K3TF0Q2jSd94uSZ60kK1e3qyVpQK6PVWXp2/FC3mp6jBhKKOiY2h3gtUV64TWM6wDETRPLDfSakXmH3w8g9Jlug8ZtTt4kVF0kLUYYmCCtD/DrQ5YhMGbA9L3ucdjh0y8kOHW5gU/VEEmJTcL4Pz/f7mgoAbYkAAAAAElFTkSuQmCC"] +}' +``` + +#### Response + +```json +{ + "model": "llava", + "created_at": "2023-11-03T15:36:02.583064Z", + "response": "A happy cartoon character, which is cute and cheerful.", + "done": true, + "context": [1, 2, 3], + "total_duration": 2938432250, + "load_duration": 2559292, + "prompt_eval_count": 1, + "prompt_eval_duration": 2195557000, + "eval_count": 44, + "eval_duration": 736432000 +} +``` + +#### Request (Raw Mode) + +In some cases, you may wish to bypass the templating system and provide a full prompt. In this case, you can use the `raw` parameter to disable templating. Also note that raw mode will not return a context. + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "mistral", + "prompt": "[INST] why is the sky blue? [/INST]", + "raw": true, + "stream": false +}' +``` + +#### Request (Reproducible outputs) + +For reproducible outputs, set `seed` to a number: + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "mistral", + "prompt": "Why is the sky blue?", + "options": { + "seed": 123 + } +}' +``` + +##### Response + +```json +{ + "model": "mistral", + "created_at": "2023-11-03T15:36:02.583064Z", + "response": " The sky appears blue because of a phenomenon called Rayleigh scattering.", + "done": true, + "total_duration": 8493852375, + "load_duration": 6589624375, + "prompt_eval_count": 14, + "prompt_eval_duration": 119039000, + "eval_count": 110, + "eval_duration": 1779061000 +} +``` + +#### Generate request (With options) + +If you want to set custom options for the model at runtime rather than in the Modelfile, you can do so with the `options` parameter. This example sets every available option, but you can set any of them individually and omit the ones you do not want to override. + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llama3.2", + "prompt": "Why is the sky blue?", + "stream": false, + "options": { + "num_keep": 5, + "seed": 42, + "num_predict": 100, + "top_k": 20, + "top_p": 0.9, + "min_p": 0.0, + "typical_p": 0.7, + "repeat_last_n": 33, + "temperature": 0.8, + "repeat_penalty": 1.2, + "presence_penalty": 1.5, + "frequency_penalty": 1.0, + "mirostat": 1, + "mirostat_tau": 0.8, + "mirostat_eta": 0.6, + "penalize_newline": true, + "stop": ["\n", "user:"], + "numa": false, + "num_ctx": 1024, + "num_batch": 2, + "num_gpu": 1, + "main_gpu": 0, + "low_vram": false, + "vocab_only": false, + "use_mmap": true, + "use_mlock": false, + "num_thread": 8 + } +}' +``` + +##### Response + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T19:22:45.499127Z", + "response": "The sky is blue because it is the color of the sky.", + "done": true, + "context": [1, 2, 3], + "total_duration": 4935886791, + "load_duration": 534986708, + "prompt_eval_count": 26, + "prompt_eval_duration": 107345000, + "eval_count": 237, + "eval_duration": 4289432000 +} +``` + +#### Load a model + +If an empty prompt is provided, the model will be loaded into memory. + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llama3.2" +}' +``` + +##### Response + +A single JSON object is returned: + +```json +{ + "model": "llama3.2", + "created_at": "2023-12-18T19:52:07.071755Z", + "response": "", + "done": true +} +``` + +#### Unload a model + +If an empty prompt is provided and the `keep_alive` parameter is set to `0`, a model will be unloaded from memory. + +##### Request + +```shell +curl http://localhost:11434/api/generate -d '{ + "model": "llama3.2", + "keep_alive": 0 +}' +``` + +##### Response + +A single JSON object is returned: + +```json +{ + "model": "llama3.2", + "created_at": "2024-09-12T03:54:03.516566Z", + "response": "", + "done": true, + "done_reason": "unload" +} +``` + +## Generate a chat completion + +``` +POST /api/chat +``` + +Generate the next message in a chat with a provided model. This is a streaming endpoint, so there will be a series of responses. Streaming can be disabled using `"stream": false`. The final response object will include statistics and additional data from the request. + +### Parameters + +- `model`: (required) the [model name](#model-names) +- `messages`: the messages of the chat, this can be used to keep a chat memory +- `tools`: list of tools in JSON for the model to use if supported + +The `message` object has the following fields: + +- `role`: the role of the message, either `system`, `user`, `assistant`, or `tool` +- `content`: the content of the message +- `images` (optional): a list of images to include in the message (for multimodal models such as `llava`) +- `tool_calls` (optional): a list of tools in JSON that the model wants to use + +Advanced parameters (optional): + +- `format`: the format to return a response in. Format can be `json` or a JSON schema. +- `options`: additional model parameters listed in the documentation for the [Modelfile](./modelfile.md#valid-parameters-and-values) such as `temperature` +- `stream`: if `false` the response will be returned as a single response object, rather than a stream of objects +- `keep_alive`: controls how long the model will stay loaded into memory following the request (default: `5m`) + +### Structured outputs + +Structured outputs are supported by providing a JSON schema in the `format` parameter. The model will generate a response that matches the schema. See the [Chat request (Structured outputs)](#chat-request-structured-outputs) example below. + +### Examples + +#### Chat Request (Streaming) + +##### Request + +Send a chat message with a streaming response. + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [ + { + "role": "user", + "content": "why is the sky blue?" + } + ] +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T08:52:19.385406455-07:00", + "message": { + "role": "assistant", + "content": "The", + "images": null + }, + "done": false +} +``` + +Final response: + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T19:22:45.499127Z", + "message": { + "role": "assistant", + "content": "" + }, + "done": true, + "total_duration": 4883583458, + "load_duration": 1334875, + "prompt_eval_count": 26, + "prompt_eval_duration": 342546000, + "eval_count": 282, + "eval_duration": 4535599000 +} +``` + +#### Chat request (No streaming) + +##### Request + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [ + { + "role": "user", + "content": "why is the sky blue?" + } + ], + "stream": false +}' +``` + +##### Response + +```json +{ + "model": "llama3.2", + "created_at": "2023-12-12T14:13:43.416799Z", + "message": { + "role": "assistant", + "content": "Hello! How are you today?" + }, + "done": true, + "total_duration": 5191566416, + "load_duration": 2154458, + "prompt_eval_count": 26, + "prompt_eval_duration": 383809000, + "eval_count": 298, + "eval_duration": 4799921000 +} +``` + +#### Chat request (Structured outputs) + +##### Request + +```shell +curl -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{ + "model": "llama3.1", + "messages": [{"role": "user", "content": "Ollama is 22 years old and busy saving the world. Return a JSON object with the age and availability."}], + "stream": false, + "format": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "available": { + "type": "boolean" + } + }, + "required": [ + "age", + "available" + ] + }, + "options": { + "temperature": 0 + } +}' +``` + +##### Response + +```json +{ + "model": "llama3.1", + "created_at": "2024-12-06T00:46:58.265747Z", + "message": { "role": "assistant", "content": "{\"age\": 22, \"available\": false}" }, + "done_reason": "stop", + "done": true, + "total_duration": 2254970291, + "load_duration": 574751416, + "prompt_eval_count": 34, + "prompt_eval_duration": 1502000000, + "eval_count": 12, + "eval_duration": 175000000 +} +``` + +#### Chat request (With History) + +Send a chat message with a conversation history. You can use this same approach to start the conversation using multi-shot or chain-of-thought prompting. + +##### Request + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [ + { + "role": "user", + "content": "why is the sky blue?" + }, + { + "role": "assistant", + "content": "due to rayleigh scattering." + }, + { + "role": "user", + "content": "how is that different than mie scattering?" + } + ] +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T08:52:19.385406455-07:00", + "message": { + "role": "assistant", + "content": "The" + }, + "done": false +} +``` + +Final response: + +```json +{ + "model": "llama3.2", + "created_at": "2023-08-04T19:22:45.499127Z", + "done": true, + "total_duration": 8113331500, + "load_duration": 6396458, + "prompt_eval_count": 61, + "prompt_eval_duration": 398801000, + "eval_count": 468, + "eval_duration": 7701267000 +} +``` + +#### Chat request (with images) + +##### Request + +Send a chat message with images. The images should be provided as an array, with the individual images encoded in Base64. + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llava", + "messages": [ + { + "role": "user", + "content": "what is in this image?", + "images": ["iVBORw0KGgoAAAANSUhEUgAAAG0AAABmCAYAAADBPx+VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAA3VSURBVHgB7Z27r0zdG8fX743i1bi1ikMoFMQloXRpKFFIqI7LH4BEQ+NWIkjQuSWCRIEoULk0gsK1kCBI0IhrQVT7tz/7zZo888yz1r7MnDl7z5xvsjkzs2fP3uu71nNfa7lkAsm7d++Sffv2JbNmzUqcc8m0adOSzZs3Z+/XES4ZckAWJEGWPiCxjsQNLWmQsWjRIpMseaxcuTKpG/7HP27I8P79e7dq1ars/yL4/v27S0ejqwv+cUOGEGGpKHR37tzJCEpHV9tnT58+dXXCJDdECBE2Ojrqjh071hpNECjx4cMHVycM1Uhbv359B2F79+51586daxN/+pyRkRFXKyRDAqxEp4yMlDDzXG1NPnnyJKkThoK0VFd1ELZu3TrzXKxKfW7dMBQ6bcuWLW2v0VlHjx41z717927ba22U9APcw7Nnz1oGEPeL3m3p2mTAYYnFmMOMXybPPXv2bNIPpFZr1NHn4HMw0KRBjg9NuRw95s8PEcz/6DZELQd/09C9QGq5RsmSRybqkwHGjh07OsJSsYYm3ijPpyHzoiacg35MLdDSIS/O1yM778jOTwYUkKNHWUzUWaOsylE00MyI0fcnOwIdjvtNdW/HZwNLGg+sR1kMepSNJXmIwxBZiG8tDTpEZzKg0GItNsosY8USkxDhD0Rinuiko2gfL/RbiD2LZAjU9zKQJj8RDR0vJBR1/Phx9+PHj9Z7REF4nTZkxzX4LCXHrV271qXkBAPGfP/atWvu/PnzHe4C97F48eIsRLZ9+3a3f/9+87dwP1JxaF7/3r17ba+5l4EcaVo0lj3SBq5kGTJSQmLWMjgYNei2GPT1MuMqGTDEFHzeQSP2wi/jGnkmPJ/nhccs44jvDAxpVcxnq0F6eT8h4ni/iIWpR5lPyA6ETkNXoSukvpJAD3AsXLiwpZs49+fPn5ke4j10TqYvegSfn0OnafC+Tv9ooA/JPkgQysqQNBzagXY55nO/oa1F7qvIPWkRL12WRpMWUvpVDYmxAPehxWSe8ZEXL20sadYIozfmNch4QJPAfeJgW3rNsnzphBKNJM2KKODo1rVOMRYik5ETy3ix4qWNI81qAAirizgMIc+yhTytx0JWZuNI03qsrgWlGtwjoS9XwgUhWGyhUaRZZQNNIEwCiXD16tXcAHUs79co0vSD8rrJCIW98pzvxpAWyyo3HYwqS0+H0BjStClcZJT5coMm6D2LOF8TolGJtK9fvyZpyiC5ePFi9nc/oJU4eiEP0jVoAnHa9wyJycITMP78+eMeP37sXrx44d6+fdt6f82aNdkx1pg9e3Zb5W+RSRE+n+VjksQWifvVaTKFhn5O8my63K8Qabdv33b379/PiAP//vuvW7BggZszZ072/+TJk91YgkafPn166zXB1rQHFvouAWHq9z3SEevSUerqCn2/dDCeta2jxYbr69evk4MHDyY7d+7MjhMnTiTPnz9Pfv/+nfQT2ggpO2dMF8cghuoM7Ygj5iWCqRlGFml0QC/ftGmTmzt3rmsaKDsgBSPh0/8yPeLLBihLkOKJc0jp8H8vUzcxIA1k6QJ/c78tWEyj5P3o4u9+jywNPdJi5rAH9x0KHcl4Hg570eQp3+vHXGyrmEeigzQsQsjavXt38ujRo44LQuDDhw+TW7duRS1HGgMxhNXHgflaNTOsHyKvHK5Ijo2jbFjJBQK9YwFd6RVMzfgRBmEfP37suBBm/p49e1qjEP2mwTViNRo0VJWH1deMXcNK08uUjVUu7s/zRaL+oLNxz1bpANco4npUgX4G2eFbpDFyQoQxojBCpEGSytmOH8qrH5Q9vuzD6ofQylkCUmh8DBAr+q8JCyVNtWQIidKQE9wNtLSQnS4jDSsxNHogzFuQBw4cyM61UKVsjfr3ooBkPSqqQHesUPWVtzi9/vQi1T+rJj7WiTz4Pt/l3LxUkr5P2VYZaZ4URpsE+st/dujQoaBBYokbrz/8TJNQYLSonrPS9kUaSkPeZyj1AWSj+d+VBoy1pIWVNed8P0Ll/ee5HdGRhrHhR5GGN0r4LGZBaj8oFDJitBTJzIZgFcmU0Y8ytWMZMzJOaXUSrUs5RxKnrxmbb5YXO9VGUhtpXldhEUogFr3IzIsvlpmdosVcGVGXFWp2oU9kLFL3dEkSz6NHEY1sjSRdIuDFWEhd8KxFqsRi1uM/nz9/zpxnwlESONdg6dKlbsaMGS4EHFHtjFIDHwKOo46l4TxSuxgDzi+rE2jg+BaFruOX4HXa0Nnf1lwAPufZeF8/r6zD97WK2qFnGjBxTw5qNGPxT+5T/r7/7RawFC3j4vTp09koCxkeHjqbHJqArmH5UrFKKksnxrK7FuRIs8STfBZv+luugXZ2pR/pP9Ois4z+TiMzUUkUjD0iEi1fzX8GmXyuxUBRcaUfykV0YZnlJGKQpOiGB76x5GeWkWWJc3mOrK6S7xdND+W5N6XyaRgtWJFe13GkaZnKOsYqGdOVVVbGupsyA/l7emTLHi7vwTdirNEt0qxnzAvBFcnQF16xh/TMpUuXHDowhlA9vQVraQhkudRdzOnK+04ZSP3DUhVSP61YsaLtd/ks7ZgtPcXqPqEafHkdqa84X6aCeL7YWlv6edGFHb+ZFICPlljHhg0bKuk0CSvVznWsotRu433alNdFrqG45ejoaPCaUkWERpLXjzFL2Rpllp7PJU2a/v7Ab8N05/9t27Z16KUqoFGsxnI9EosS2niSYg9SpU6B4JgTrvVW1flt1sT+0ADIJU2maXzcUTraGCRaL1Wp9rUMk16PMom8QhruxzvZIegJjFU7LLCePfS8uaQdPny4jTTL0dbee5mYokQsXTIWNY46kuMbnt8Kmec+LGWtOVIl9cT1rCB0V8WqkjAsRwta93TbwNYoGKsUSChN44lgBNCoHLHzquYKrU6qZ8lolCIN0Rh6cP0Q3U6I6IXILYOQI513hJaSKAorFpuHXJNfVlpRtmYBk1Su1obZr5dnKAO+L10Hrj3WZW+E3qh6IszE37F6EB+68mGpvKm4eb9bFrlzrok7fvr0Kfv727dvWRmdVTJHw0qiiCUSZ6wCK+7XL/AcsgNyL74DQQ730sv78Su7+t/A36MdY0sW5o40ahslXr58aZ5HtZB8GH64m9EmMZ7FpYw4T6QnrZfgenrhFxaSiSGXtPnz57e9TkNZLvTjeqhr734CNtrK41L40sUQckmj1lGKQ0rC37x544r8eNXRpnVE3ZZY7zXo8NomiO0ZUCj2uHz58rbXoZ6gc0uA+F6ZeKS/jhRDUq8MKrTho9fEkihMmhxtBI1DxKFY9XLpVcSkfoi8JGnToZO5sU5aiDQIW716ddt7ZLYtMQlhECdBGXZZMWldY5BHm5xgAroWj4C0hbYkSc/jBmggIrXJWlZM6pSETsEPGqZOndr2uuuR5rF169a2HoHPdurUKZM4CO1WTPqaDaAd+GFGKdIQkxAn9RuEWcTRyN2KSUgiSgF5aWzPTeA/lN5rZubMmR2bE4SIC4nJoltgAV/dVefZm72AtctUCJU2CMJ327hxY9t7EHbkyJFseq+EJSY16RPo3Dkq1kkr7+q0bNmyDuLQcZBEPYmHVdOBiJyIlrRDq41YPWfXOxUysi5fvtyaj+2BpcnsUV/oSoEMOk2CQGlr4ckhBwaetBhjCwH0ZHtJROPJkyc7UjcYLDjmrH7ADTEBXFfOYmB0k9oYBOjJ8b4aOYSe7QkKcYhFlq3QYLQhSidNmtS2RATwy8YOM3EQJsUjKiaWZ+vZToUQgzhkHXudb/PW5YMHD9yZM2faPsMwoc7RciYJXbGuBqJ1UIGKKLv915jsvgtJxCZDubdXr165mzdvtr1Hz5LONA8jrUwKPqsmVesKa49S3Q4WxmRPUEYdTjgiUcfUwLx589ySJUva3oMkP6IYddq6HMS4o55xBJBUeRjzfa4Zdeg56QZ43LhxoyPo7Lf1kNt7oO8wWAbNwaYjIv5lhyS7kRf96dvm5Jah8vfvX3flyhX35cuX6HfzFHOToS1H4BenCaHvO8pr8iDuwoUL7tevX+b5ZdbBair0xkFIlFDlW4ZknEClsp/TzXyAKVOmmHWFVSbDNw1l1+4f90U6IY/q4V27dpnE9bJ+v87QEydjqx/UamVVPRG+mwkNTYN+9tjkwzEx+atCm/X9WvWtDtAb68Wy9LXa1UmvCDDIpPkyOQ5ZwSzJ4jMrvFcr0rSjOUh+GcT4LSg5ugkW1Io0/SCDQBojh0hPlaJdah+tkVYrnTZowP8iq1F1TgMBBauufyB33x1v+NWFYmT5KmppgHC+NkAgbmRkpD3yn9QIseXymoTQFGQmIOKTxiZIWpvAatenVqRVXf2nTrAWMsPnKrMZHz6bJq5jvce6QK8J1cQNgKxlJapMPdZSR64/UivS9NztpkVEdKcrs5alhhWP9NeqlfWopzhZScI6QxseegZRGeg5a8C3Re1Mfl1ScP36ddcUaMuv24iOJtz7sbUjTS4qBvKmstYJoUauiuD3k5qhyr7QdUHMeCgLa1Ear9NquemdXgmum4fvJ6w1lqsuDhNrg1qSpleJK7K3TF0Q2jSd94uSZ60kK1e3qyVpQK6PVWXp2/FC3mp6jBhKKOiY2h3gtUV64TWM6wDETRPLDfSakXmH3w8g9Jlug8ZtTt4kVF0kLUYYmCCtD/DrQ5YhMGbA9L3ucdjh0y8kOHW5gU/VEEmJTcL4Pz/f7mgoAbYkAAAAAElFTkSuQmCC"] + } + ] +}' +``` + +##### Response + +```json +{ + "model": "llava", + "created_at": "2023-12-13T22:42:50.203334Z", + "message": { + "role": "assistant", + "content": " The image features a cute, little pig with an angry facial expression. It's wearing a heart on its shirt and is waving in the air. This scene appears to be part of a drawing or sketching project.", + "images": null + }, + "done": true, + "total_duration": 1668506709, + "load_duration": 1986209, + "prompt_eval_count": 26, + "prompt_eval_duration": 359682000, + "eval_count": 83, + "eval_duration": 1303285000 +} +``` + +#### Chat request (Reproducible outputs) + +##### Request + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ], + "options": { + "seed": 101, + "temperature": 0 + } +}' +``` + +##### Response + +```json +{ + "model": "llama3.2", + "created_at": "2023-12-12T14:13:43.416799Z", + "message": { + "role": "assistant", + "content": "Hello! How are you today?" + }, + "done": true, + "total_duration": 5191566416, + "load_duration": 2154458, + "prompt_eval_count": 26, + "prompt_eval_duration": 383809000, + "eval_count": 298, + "eval_duration": 4799921000 +} +``` + +#### Chat request (with tools) + +##### Request + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [ + { + "role": "user", + "content": "What is the weather today in Paris?" + } + ], + "stream": false, + "tools": [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather for a location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The location to get the weather for, e.g. San Francisco, CA" + }, + "format": { + "type": "string", + "description": "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'", + "enum": ["celsius", "fahrenheit"] + } + }, + "required": ["location", "format"] + } + } + } + ] +}' +``` + +##### Response + +```json +{ + "model": "llama3.2", + "created_at": "2024-07-22T20:33:28.123648Z", + "message": { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "function": { + "name": "get_current_weather", + "arguments": { + "format": "celsius", + "location": "Paris, FR" + } + } + } + ] + }, + "done_reason": "stop", + "done": true, + "total_duration": 885095291, + "load_duration": 3753500, + "prompt_eval_count": 122, + "prompt_eval_duration": 328493000, + "eval_count": 33, + "eval_duration": 552222000 +} +``` + +#### Load a model + +If the messages array is empty, the model will be loaded into memory. + +##### Request + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [] +}' +``` + +##### Response + +```json +{ + "model": "llama3.2", + "created_at":"2024-09-12T21:17:29.110811Z", + "message": { + "role": "assistant", + "content": "" + }, + "done_reason": "load", + "done": true +} +``` + +#### Unload a model + +If the messages array is empty and the `keep_alive` parameter is set to `0`, a model will be unloaded from memory. + +##### Request + +```shell +curl http://localhost:11434/api/chat -d '{ + "model": "llama3.2", + "messages": [], + "keep_alive": 0 +}' +``` + +##### Response + +A single JSON object is returned: + +```json +{ + "model": "llama3.2", + "created_at":"2024-09-12T21:33:17.547535Z", + "message": { + "role": "assistant", + "content": "" + }, + "done_reason": "unload", + "done": true +} +``` + +## Create a Model + +``` +POST /api/create +``` + +Create a model from: + * another model; + * a safetensors directory; or + * a GGUF file. + +If you are creating a model from a safetensors directory or from a GGUF file, you must [create a blob](#create-a-blob) for each of the files and then use the file name and SHA256 digest associated with each blob in the `files` field. + +### Parameters + +- `model`: name of the model to create +- `from`: (optional) name of an existing model to create the new model from +- `files`: (optional) a dictionary of file names to SHA256 digests of blobs to create the model from +- `adapters`: (optional) a dictionary of file names to SHA256 digests of blobs for LORA adapters +- `template`: (optional) the prompt template for the model +- `license`: (optional) a string or list of strings containing the license or licenses for the model +- `system`: (optional) a string containing the system prompt for the model +- `parameters`: (optional) a dictionary of parameters for the model (see [Modelfile](./modelfile.md#valid-parameters-and-values) for a list of parameters) +- `messages`: (optional) a list of message objects used to create a conversation +- `stream`: (optional) if `false` the response will be returned as a single response object, rather than a stream of objects +- `quantize` (optional): quantize a non-quantized (e.g. float16) model + +#### Quantization types + +| Type | Recommended | +| --- | :-: | +| q2_K | | +| q3_K_L | | +| q3_K_M | | +| q3_K_S | | +| q4_0 | | +| q4_1 | | +| q4_K_M | * | +| q4_K_S | | +| q5_0 | | +| q5_1 | | +| q5_K_M | | +| q5_K_S | | +| q6_K | | +| q8_0 | * | + +### Examples + +#### Create a new model + +Create a new model from an existing model. + +##### Request + +```shell +curl http://localhost:11434/api/create -d '{ + "model": "mario", + "from": "llama3.2", + "system": "You are Mario from Super Mario Bros." +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```json +{"status":"reading model metadata"} +{"status":"creating system layer"} +{"status":"using already created layer sha256:22f7f8ef5f4c791c1b03d7eb414399294764d7cc82c7e94aa81a1feb80a983a2"} +{"status":"using already created layer sha256:8c17c2ebb0ea011be9981cc3922db8ca8fa61e828c5d3f44cb6ae342bf80460b"} +{"status":"using already created layer sha256:7c23fb36d80141c4ab8cdbb61ee4790102ebd2bf7aeff414453177d4f2110e5d"} +{"status":"using already created layer sha256:2e0493f67d0c8c9c68a8aeacdf6a38a2151cb3c4c1d42accf296e19810527988"} +{"status":"using already created layer sha256:2759286baa875dc22de5394b4a925701b1896a7e3f8e53275c36f75a877a82c9"} +{"status":"writing layer sha256:df30045fe90f0d750db82a058109cecd6d4de9c90a3d75b19c09e5f64580bb42"} +{"status":"writing layer sha256:f18a68eb09bf925bb1b669490407c1b1251c5db98dc4d3d81f3088498ea55690"} +{"status":"writing manifest"} +{"status":"success"} +``` + +#### Quantize a model + +Quantize a non-quantized model. + +##### Request + +```shell +curl http://localhost:11434/api/create -d '{ + "model": "llama3.1:quantized", + "from": "llama3.1:8b-instruct-fp16", + "quantize": "q4_K_M" +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```json +{"status":"quantizing F16 model to Q4_K_M"} +{"status":"creating new layer sha256:667b0c1932bc6ffc593ed1d03f895bf2dc8dc6df21db3042284a6f4416b06a29"} +{"status":"using existing layer sha256:11ce4ee3e170f6adebac9a991c22e22ab3f8530e154ee669954c4bc73061c258"} +{"status":"using existing layer sha256:0ba8f0e314b4264dfd19df045cde9d4c394a52474bf92ed6a3de22a4ca31a177"} +{"status":"using existing layer sha256:56bb8bd477a519ffa694fc449c2413c6f0e1d3b1c88fa7e3c9d88d3ae49d4dcb"} +{"status":"creating new layer sha256:455f34728c9b5dd3376378bfb809ee166c145b0b4c1f1a6feca069055066ef9a"} +{"status":"writing manifest"} +{"status":"success"} +``` + +#### Create a model from GGUF + +Create a model from a GGUF file. The `files` parameter should be filled out with the file name and SHA256 digest of the GGUF file you wish to use. Use [/api/blobs/:digest](#push-a-blob) to push the GGUF file to the server before calling this API. + + +##### Request + +```shell +curl http://localhost:11434/api/create -d '{ + "model": "my-gguf-model", + "files": { + "test.gguf": "sha256:432f310a77f4650a88d0fd59ecdd7cebed8d684bafea53cbff0473542964f0c3" + } +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```json +{"status":"parsing GGUF"} +{"status":"using existing layer sha256:432f310a77f4650a88d0fd59ecdd7cebed8d684bafea53cbff0473542964f0c3"} +{"status":"writing manifest"} +{"status":"success"} +``` + + +#### Create a model from a Safetensors directory + +The `files` parameter should include a dictionary of files for the safetensors model which includes the file names and SHA256 digest of each file. Use [/api/blobs/:digest](#push-a-blob) to first push each of the files to the server before calling this API. Files will remain in the cache until the Ollama server is restarted. + +##### Request + +```shell +curl http://localhost:11434/api/create -d '{ + "model": "fred", + "files": { + "config.json": "sha256:dd3443e529fb2290423a0c65c2d633e67b419d273f170259e27297219828e389", + "generation_config.json": "sha256:88effbb63300dbbc7390143fbbdd9d9fa50587b37e8bfd16c8c90d4970a74a36", + "special_tokens_map.json": "sha256:b7455f0e8f00539108837bfa586c4fbf424e31f8717819a6798be74bef813d05", + "tokenizer.json": "sha256:bbc1904d35169c542dffbe1f7589a5994ec7426d9e5b609d07bab876f32e97ab", + "tokenizer_config.json": "sha256:24e8a6dc2547164b7002e3125f10b415105644fcf02bf9ad8b674c87b1eaaed6", + "model.safetensors": "sha256:1ff795ff6a07e6a68085d206fb84417da2f083f68391c2843cd2b8ac6df8538f" + } +}' +``` + +##### Response + +A stream of JSON objects is returned: + +```shell +{"status":"converting model"} +{"status":"creating new layer sha256:05ca5b813af4a53d2c2922933936e398958855c44ee534858fcfd830940618b6"} +{"status":"using autodetected template llama3-instruct"} +{"status":"using existing layer sha256:56bb8bd477a519ffa694fc449c2413c6f0e1d3b1c88fa7e3c9d88d3ae49d4dcb"} +{"status":"writing manifest"} +{"status":"success"} +``` + +## Check if a Blob Exists + +```shell +HEAD /api/blobs/:digest +``` + +Ensures that the file blob (Binary Large Object) used with create a model exists on the server. This checks your Ollama server and not ollama.com. + +### Query Parameters + +- `digest`: the SHA256 digest of the blob + +### Examples + +#### Request + +```shell +curl -I http://localhost:11434/api/blobs/sha256:29fdb92e57cf0827ded04ae6461b5931d01fa595843f55d36f5b275a52087dd2 +``` + +#### Response + +Return 200 OK if the blob exists, 404 Not Found if it does not. + +## Push a Blob + +``` +POST /api/blobs/:digest +``` + +Push a file to the Ollama server to create a "blob" (Binary Large Object). + +### Query Parameters + +- `digest`: the expected SHA256 digest of the file + +### Examples + +#### Request + +```shell +curl -T model.gguf -X POST http://localhost:11434/api/blobs/sha256:29fdb92e57cf0827ded04ae6461b5931d01fa595843f55d36f5b275a52087dd2 +``` + +#### Response + +Return 201 Created if the blob was successfully created, 400 Bad Request if the digest used is not expected. + +## List Local Models + +``` +GET /api/tags +``` + +List models that are available locally. + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/tags +``` + +#### Response + +A single JSON object will be returned. + +```json +{ + "models": [ + { + "name": "codellama:13b", + "modified_at": "2023-11-04T14:56:49.277302595-07:00", + "size": 7365960935, + "digest": "9f438cb9cd581fc025612d27f7c1a6669ff83a8bb0ed86c94fcf4c5440555697", + "details": { + "format": "gguf", + "family": "llama", + "families": null, + "parameter_size": "13B", + "quantization_level": "Q4_0" + } + }, + { + "name": "llama3:latest", + "modified_at": "2023-12-07T09:32:18.757212583-08:00", + "size": 3825819519, + "digest": "fe938a131f40e6f6d40083c9f0f430a515233eb2edaa6d72eb85c50d64f2300e", + "details": { + "format": "gguf", + "family": "llama", + "families": null, + "parameter_size": "7B", + "quantization_level": "Q4_0" + } + } + ] +} +``` + +## Show Model Information + +``` +POST /api/show +``` + +Show information about a model including details, modelfile, template, parameters, license, system prompt. + +### Parameters + +- `model`: name of the model to show +- `verbose`: (optional) if set to `true`, returns full data for verbose response fields + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/show -d '{ + "model": "llava" +}' +``` + +#### Response + +```json +{ + "modelfile": "# Modelfile generated by \"ollama show\"\n# To build a new Modelfile based on this one, replace the FROM line with:\n# FROM llava:latest\n\nFROM /Users/matt/.ollama/models/blobs/sha256:200765e1283640ffbd013184bf496e261032fa75b99498a9613be4e94d63ad52\nTEMPLATE \"\"\"{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: \"\"\"\nPARAMETER num_ctx 4096\nPARAMETER stop \"\u003c/s\u003e\"\nPARAMETER stop \"USER:\"\nPARAMETER stop \"ASSISTANT:\"", + "parameters": "num_keep 24\nstop \"<|start_header_id|>\"\nstop \"<|end_header_id|>\"\nstop \"<|eot_id|>\"", + "template": "{{ if .System }}<|start_header_id|>system<|end_header_id|>\n\n{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>\n\n{{ .Response }}<|eot_id|>", + "details": { + "parent_model": "", + "format": "gguf", + "family": "llama", + "families": [ + "llama" + ], + "parameter_size": "8.0B", + "quantization_level": "Q4_0" + }, + "model_info": { + "general.architecture": "llama", + "general.file_type": 2, + "general.parameter_count": 8030261248, + "general.quantization_version": 2, + "llama.attention.head_count": 32, + "llama.attention.head_count_kv": 8, + "llama.attention.layer_norm_rms_epsilon": 0.00001, + "llama.block_count": 32, + "llama.context_length": 8192, + "llama.embedding_length": 4096, + "llama.feed_forward_length": 14336, + "llama.rope.dimension_count": 128, + "llama.rope.freq_base": 500000, + "llama.vocab_size": 128256, + "tokenizer.ggml.bos_token_id": 128000, + "tokenizer.ggml.eos_token_id": 128009, + "tokenizer.ggml.merges": [], // populates if `verbose=true` + "tokenizer.ggml.model": "gpt2", + "tokenizer.ggml.pre": "llama-bpe", + "tokenizer.ggml.token_type": [], // populates if `verbose=true` + "tokenizer.ggml.tokens": [] // populates if `verbose=true` + }, + "capabilities": [ + "completion", + "vision" + ], +} +``` + +## Copy a Model + +``` +POST /api/copy +``` + +Copy a model. Creates a model with another name from an existing model. + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/copy -d '{ + "source": "llama3.2", + "destination": "llama3-backup" +}' +``` + +#### Response + +Returns a 200 OK if successful, or a 404 Not Found if the source model doesn't exist. + +## Delete a Model + +``` +DELETE /api/delete +``` + +Delete a model and its data. + +### Parameters + +- `model`: model name to delete + +### Examples + +#### Request + +```shell +curl -X DELETE http://localhost:11434/api/delete -d '{ + "model": "llama3:13b" +}' +``` + +#### Response + +Returns a 200 OK if successful, 404 Not Found if the model to be deleted doesn't exist. + +## Pull a Model + +``` +POST /api/pull +``` + +Download a model from the ollama library. Cancelled pulls are resumed from where they left off, and multiple calls will share the same download progress. + +### Parameters + +- `model`: name of the model to pull +- `insecure`: (optional) allow insecure connections to the library. Only use this if you are pulling from your own library during development. +- `stream`: (optional) if `false` the response will be returned as a single response object, rather than a stream of objects + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/pull -d '{ + "model": "llama3.2" +}' +``` + +#### Response + +If `stream` is not specified, or set to `true`, a stream of JSON objects is returned: + +The first object is the manifest: + +```json +{ + "status": "pulling manifest" +} +``` + +Then there is a series of downloading responses. Until any of the download is completed, the `completed` key may not be included. The number of files to be downloaded depends on the number of layers specified in the manifest. + +```json +{ + "status": "downloading digestname", + "digest": "digestname", + "total": 2142590208, + "completed": 241970 +} +``` + +After all the files are downloaded, the final responses are: + +```json +{ + "status": "verifying sha256 digest" +} +{ + "status": "writing manifest" +} +{ + "status": "removing any unused layers" +} +{ + "status": "success" +} +``` + +if `stream` is set to false, then the response is a single JSON object: + +```json +{ + "status": "success" +} +``` + +## Push a Model + +``` +POST /api/push +``` + +Upload a model to a model library. Requires registering for ollama.ai and adding a public key first. + +### Parameters + +- `model`: name of the model to push in the form of `/:` +- `insecure`: (optional) allow insecure connections to the library. Only use this if you are pushing to your library during development. +- `stream`: (optional) if `false` the response will be returned as a single response object, rather than a stream of objects + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/push -d '{ + "model": "mattw/pygmalion:latest" +}' +``` + +#### Response + +If `stream` is not specified, or set to `true`, a stream of JSON objects is returned: + +```json +{ "status": "retrieving manifest" } +``` + +and then: + +```json +{ + "status": "starting upload", + "digest": "sha256:bc07c81de745696fdf5afca05e065818a8149fb0c77266fb584d9b2cba3711ab", + "total": 1928429856 +} +``` + +Then there is a series of uploading responses: + +```json +{ + "status": "starting upload", + "digest": "sha256:bc07c81de745696fdf5afca05e065818a8149fb0c77266fb584d9b2cba3711ab", + "total": 1928429856 +} +``` + +Finally, when the upload is complete: + +```json +{"status":"pushing manifest"} +{"status":"success"} +``` + +If `stream` is set to `false`, then the response is a single JSON object: + +```json +{ "status": "success" } +``` + +## Generate Embeddings + +``` +POST /api/embed +``` + +Generate embeddings from a model + +### Parameters + +- `model`: name of model to generate embeddings from +- `input`: text or list of text to generate embeddings for + +Advanced parameters: + +- `truncate`: truncates the end of each input to fit within context length. Returns error if `false` and context length is exceeded. Defaults to `true` +- `options`: additional model parameters listed in the documentation for the [Modelfile](./modelfile.md#valid-parameters-and-values) such as `temperature` +- `keep_alive`: controls how long the model will stay loaded into memory following the request (default: `5m`) + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/embed -d '{ + "model": "all-minilm", + "input": "Why is the sky blue?" +}' +``` + +#### Response + +```json +{ + "model": "all-minilm", + "embeddings": [[ + 0.010071029, -0.0017594862, 0.05007221, 0.04692972, 0.054916814, + 0.008599704, 0.105441414, -0.025878139, 0.12958129, 0.031952348 + ]], + "total_duration": 14143917, + "load_duration": 1019500, + "prompt_eval_count": 8 +} +``` + +#### Request (Multiple input) + +```shell +curl http://localhost:11434/api/embed -d '{ + "model": "all-minilm", + "input": ["Why is the sky blue?", "Why is the grass green?"] +}' +``` + +#### Response + +```json +{ + "model": "all-minilm", + "embeddings": [[ + 0.010071029, -0.0017594862, 0.05007221, 0.04692972, 0.054916814, + 0.008599704, 0.105441414, -0.025878139, 0.12958129, 0.031952348 + ],[ + -0.0098027075, 0.06042469, 0.025257962, -0.006364387, 0.07272725, + 0.017194884, 0.09032035, -0.051705178, 0.09951512, 0.09072481 + ]] +} +``` + +## List Running Models +``` +GET /api/ps +``` + +List models that are currently loaded into memory. + +#### Examples + +### Request + +```shell +curl http://localhost:11434/api/ps +``` + +#### Response + +A single JSON object will be returned. + +```json +{ + "models": [ + { + "name": "mistral:latest", + "model": "mistral:latest", + "size": 5137025024, + "digest": "2ae6f6dd7a3dd734790bbbf58b8909a606e0e7e97e94b7604e0aa7ae4490e6d8", + "details": { + "parent_model": "", + "format": "gguf", + "family": "llama", + "families": [ + "llama" + ], + "parameter_size": "7.2B", + "quantization_level": "Q4_0" + }, + "expires_at": "2024-06-04T14:38:31.83753-07:00", + "size_vram": 5137025024 + } + ] +} +``` + +## Generate Embedding + +> Note: this endpoint has been superseded by `/api/embed` + +``` +POST /api/embeddings +``` + +Generate embeddings from a model + +### Parameters + +- `model`: name of model to generate embeddings from +- `prompt`: text to generate embeddings for + +Advanced parameters: + +- `options`: additional model parameters listed in the documentation for the [Modelfile](./modelfile.md#valid-parameters-and-values) such as `temperature` +- `keep_alive`: controls how long the model will stay loaded into memory following the request (default: `5m`) + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/embeddings -d '{ + "model": "all-minilm", + "prompt": "Here is an article about llamas..." +}' +``` + +#### Response + +```json +{ + "embedding": [ + 0.5670403838157654, 0.009260174818336964, 0.23178744316101074, -0.2916173040866852, -0.8924556970596313, + 0.8785552978515625, -0.34576427936553955, 0.5742510557174683, -0.04222835972905159, -0.137906014919281 + ] +} +``` + +## Version + +``` +GET /api/version +``` + +Retrieve the Ollama version + +### Examples + +#### Request + +```shell +curl http://localhost:11434/api/version +``` + +#### Response + +```json +{ + "version": "0.5.1" +} +``` + + diff --git a/Code/pipeline-example/input/example.pdf b/Code/pipeline-example/input/example.pdf new file mode 100644 index 0000000..c3910a4 Binary files /dev/null and b/Code/pipeline-example/input/example.pdf differ diff --git a/Code/pipeline-example/main.py b/Code/pipeline-example/main.py new file mode 100644 index 0000000..098bbdc --- /dev/null +++ b/Code/pipeline-example/main.py @@ -0,0 +1,618 @@ +import os +import re +import json +from typing import List, Dict, Tuple, Union, Optional, Any, Literal + +# Add datetime import for timestamped folders +from datetime import datetime + +# Add Gemini imports +from google import genai +from google.genai import types +import dotenv # Load environment variables from .env file + +import plotly.graph_objects as go +import pandas as pd + +from sklearn.manifold import TSNE + +# Add imports for Clustering and Similarity Search +from sklearn.cluster import KMeans +from sklearn.metrics.pairwise import cosine_similarity +import numpy as np # Ensure numpy is imported if not already done earlier + +from sentence_transformers import SentenceTransformer + + +# --- Stage 1: PDF Processing --- +from pypdf import PdfReader + + +# Load environment variables from .env file +dotenv.load_dotenv() + + +def extract_text_from_pdf(pdf_path: str) -> str: + """Extracts text content from a PDF file.""" + print(f"Processing PDF: {pdf_path}") + if not os.path.exists(pdf_path): + raise FileNotFoundError(f"PDF file not found: {pdf_path}") + try: + reader = PdfReader(pdf_path) + text = "" + for page_num, page in enumerate(reader.pages): + page_text = page.extract_text() + if page_text: + # Basic cleaning: replace multiple newlines/spaces + cleaned_text = re.sub(r"\s+", " ", page_text).strip() + text += cleaned_text + "\n" # Add newline between pages + print(f" Extracted text from page {page_num + 1}") + print(f"Finished extracting text. Total length: {len(text)} characters.") + return text + except Exception as e: + print(f"Error reading PDF {pdf_path}: {e}") + raise + + +# --- Stage 2: Text Chunking --- +def chunk_text(text: str, chunk_size: int = 500, chunk_overlap: int = 50) -> List[str]: + """Splits text into overlapping chunks.""" + print(f"Chunking text (size={chunk_size}, overlap={chunk_overlap})...") + if not text: + return [] + + chunks = [] + start = 0 + while start < len(text): + end = start + chunk_size + chunk = text[start:end] + chunks.append(chunk) + start += chunk_size - chunk_overlap # Move start forward for overlap + # Ensure we don't go past the end if overlap is large + if start >= len(text) - chunk_overlap and start < len(text): + # Add the last remaining part if it wasn't fully covered + final_chunk = text[start:] + if final_chunk and ( + not chunks or chunks[-1] != final_chunk + ): # Avoid duplicates + chunks.append(final_chunk) + break # Exit loop after handling the end + + print(f"Generated {len(chunks)} chunks.") + return chunks + + +# --- Stage 3: Embedding Generation --- + +# Load a relatively small but effective model +# This will download the model the first time it's run +embedding_model = SentenceTransformer("all-MiniLM-L6-v2") +print("Embedding model loaded.") + + +def generate_embeddings(chunks: List[str]) -> Tuple[List[str], List[List[float]]]: + """Generates vector embeddings (as lists of floats) for a list of text chunks.""" + if not chunks: + return [], [] + print(f"Generating embeddings for {len(chunks)} chunks...") + # The model's encode function returns numpy arrays directly + embeddings = embedding_model.encode(chunks, show_progress_bar=True) + print(f"Generated embeddings of shape: {embeddings.shape}") + # Convert numpy array rows to list for easier simulation downstream + embeddings_list: List[List[float]] = [emb.tolist() for emb in embeddings] + return chunks, embeddings_list + + +# --- Stage 4: Simulate Supabase/pgvector Storage & Retrieval --- +# This simulates storing data, including converting vectors to pgvector's string format +# and then fetching it back, parsing the string. + +# In-memory "database" +mock_db: List[Dict[str, Any]] = [] + + +def simulate_pgvector_storage(chunks: List[str], embeddings: List[List[float]]): + """Simulates storing chunks and embeddings in a DB like Supabase.""" + print("Simulating storage...") + global mock_db + mock_db = [] # Clear previous data + for i, (chunk, embedding) in enumerate(zip(chunks, embeddings)): + # Simulate pgvector string format "[0.1,0.2,...]" + embedding_str = json.dumps(embedding) + mock_db.append( + { + "id": i, + "content": chunk, + "embedding_str": embedding_str, # Store as string + } + ) + print(f"Simulated storing {len(mock_db)} items.") + + +def parse_pgvector_string(vector_string: str) -> List[float]: + """Parses the string representation from pgvector into a list of floats.""" + try: + return json.loads(vector_string) + except (json.JSONDecodeError, TypeError) as e: + print(f"Error parsing vector string '{vector_string[:50]}...': {e}") + return [] # Return empty list on error + + +def simulate_fetch_from_db() -> Tuple[List[str], List[List[float]]]: + """Simulates fetching data and parsing vector strings.""" + print("Simulating fetching data from DB...") + fetched_chunks = [] + fetched_embeddings = [] + for item in mock_db: + content = item.get("content") + embedding_str = item.get("embedding_str") + if content and embedding_str: + parsed_embedding = parse_pgvector_string(embedding_str) + if parsed_embedding: # Only add if parsing was successful + fetched_chunks.append(content) + fetched_embeddings.append(parsed_embedding) + else: + print( + f"Warning: Failed to parse embedding for item id {item.get('id')}" + ) + + print(f"Simulated fetching {len(fetched_chunks)} items with valid embeddings.") + return fetched_chunks, fetched_embeddings + + +# --- Stage 5: Dimensionality Reduction (t-SNE) --- + + +def apply_tsne( + embeddings: Union[np.ndarray, List[List[float]]], + n_components: int = 2, + perplexity: float = 30.0, # Adjust based on number of samples + learning_rate: Union[float, Literal["auto"]] = "auto", + n_iter: int = 1000, + random_state: int = 42, + verbose: int = 1, +) -> np.ndarray: + """Applies t-SNE to reduce the dimensionality of vector embeddings.""" + print("\nApplying t-SNE...") + if isinstance(embeddings, list): + embeddings_np = np.array(embeddings, dtype=np.float32) + elif isinstance(embeddings, np.ndarray): + embeddings_np = embeddings.astype(np.float32) + else: + raise TypeError("Embeddings must be a NumPy array or a list of lists.") + + if embeddings_np.ndim != 2: + raise ValueError( + f"Input embeddings must be 2D, got shape {embeddings_np.shape}" + ) + + n_samples = embeddings_np.shape[0] + if n_samples == 0: + print("Warning: No embeddings to process with t-SNE.") + return np.empty((0, n_components)) + + # Adjust perplexity if it's too high for the number of samples + effective_perplexity = min(perplexity, max(1.0, n_samples - 1.0)) + if effective_perplexity != perplexity: + print( + f"Warning: Perplexity adjusted from {perplexity} to {effective_perplexity} due to low sample count ({n_samples})." + ) + + tsne = TSNE( + n_components=n_components, + perplexity=effective_perplexity, + learning_rate=learning_rate, + n_iter=n_iter, + init="pca", # Often more stable + random_state=random_state, + verbose=verbose, + ) + + reduced_embeddings = tsne.fit_transform(embeddings_np) + print(f"t-SNE finished. Output shape: {reduced_embeddings.shape}") + return reduced_embeddings + + +# --- Stage 6: Visualization (using Plotly) --- + +def plot_embeddings_interactive( + reduced_embeddings: np.ndarray, + texts: List[str], +): + """Creates an interactive 3D Plotly scatter plot and displays it.""" + if reduced_embeddings.shape[0] != len(texts): + raise ValueError("Number of embeddings and texts must match.") + if reduced_embeddings.shape[1] != 3: # Expect 3 dimensions now + raise ValueError("Reduced embeddings must be 3D for this plot.") + if reduced_embeddings.shape[0] == 0: + print("No data to plot.") + return + + print("\nGenerating interactive 3D plot...") + + df = pd.DataFrame( + { + "x": reduced_embeddings[:, 0], + "y": reduced_embeddings[:, 1], + "z": reduced_embeddings[:, 2], # Add z coordinate + "text": texts, + } + ) + + # Create hover text (limit length for readability) + hover_texts = [t[:200] + "..." if len(t) > 200 else t for t in df["text"]] + + fig = go.Figure( + data=go.Scatter3d( # Use Scatter3d + x=df["x"], + y=df["y"], + z=df["z"], # Add z data + mode="markers", + marker=dict( + size=5, # Adjust marker size for 3D if needed + # color=df['z'], # Example: color by z-coordinate + # colorscale='Viridis', + # showscale=True + ), + text=hover_texts, # Text shown on hover + hoverinfo="text", # Display only the hover text + ) + ) + + fig.update_layout( + title="3D t-SNE Visualization of Text Chunk Embeddings", + scene=dict( # Use scene for 3D layout + xaxis_title="t-SNE Dimension 1", + yaxis_title="t-SNE Dimension 2", + zaxis_title="t-SNE Dimension 3", # Add z-axis label + ), + hovermode="closest", + margin=dict(r=0, b=0, l=0, t=40), # Adjust margins if needed + ) + + # fig.write_html(output_filename) # Remove HTML saving + fig.show() # Display the plot in an interactive window + print("Plot window opened.") + + +# --- Gemini API Helper --- +def call_gemini_api( + prompt_text: str, model_name: str = "gemini-1.5-flash" +) -> Optional[str]: + """Calls the Gemini API with the provided text and returns the generated content.""" + try: + # Get the API key from environment variables + api_key = os.environ.get("GEMINI_API_KEY") + if not api_key: + print("Error: GEMINI_API_KEY environment variable not set.") + raise ValueError("Missing GEMINI_API_KEY") + + # Initialize the client directly with the API key + # This aligns with the README example + client = genai.Client(api_key=api_key) + + # Prepare the content for the API call + # The SDK handles converting the string prompt to the correct Content structure + contents = prompt_text + + # Set up the generation configuration object + generation_config_obj = types.GenerateContentConfig( + temperature=0.7, + response_mime_type="text/plain", # Keep as plain text, formatting instructions are in the prompt + ) + # Safety settings removed as per user request in previous step implicitly + + # Generate content using the client's models.generate_content method + # Pass the generation config object to the 'config' parameter + response = client.models.generate_content( + model=model_name, + contents=contents, + config=generation_config_obj, # Corrected parameter name + # safety_settings removed + ) + + # Check for valid response and return text + if response and response.text: + return response.text.strip() + elif response.prompt_feedback and response.prompt_feedback.block_reason: + print( + f"Warning: Gemini API call blocked. Reason: {response.prompt_feedback.block_reason}" + ) + return None + else: + # Check if the response has a 'parts' attribute (runtime check) but avoid static‑type errors + if response and hasattr(response, "parts"): + parts_attr = getattr(response, "parts") # type: ignore[attr-defined] + print( + f"Warning: Gemini API response has parts but no direct text attribute. Parts: {parts_attr}" + ) + try: + # Join text from all parts that expose a 'text' attribute + return " ".join( + part.text for part in parts_attr if hasattr(part, "text") + ).strip() + except Exception as part_error: + print(f"Error extracting text from parts: {part_error}") + return None # Fallback if parts structure is unexpected + else: + print( + f"Warning: Gemini API response format unexpected or empty: {response}" + ) + return None + + except ValueError as ve: # Catch missing API key specifically + if "Missing GEMINI_API_KEY" in str(ve): + print( + "Error: GEMINI_API_KEY environment variable not set." + ) # Ensure message is printed + raise ve # Re-raise to stop execution + else: + print(f"An unexpected value error occurred during Gemini API call: {ve}") + return None + except Exception as e: + print(f"Error calling Gemini API: {e}") + # Consider importing and catching specific google.api_core.exceptions if needed + # from google.api_core import exceptions as google_exceptions + # except google_exceptions.PermissionDenied: ... + return None + + +# --- Stage 7: Clustering and Markdown Generation --- +def generate_clustered_markdown( + reduced_embeddings: np.ndarray, + texts: List[str], + output_filename: str, # Now expects the full path including subdirectory + n_clusters: int = 5, +): + """Performs K-Means clustering, uses Gemini for topics and rewriting (Markdown/LaTeX), and saves to Markdown.""" + print("\n--- Clustering Texts and Generating Markdown with Gemini Rewriting ---") + # ... (clustering setup and KMeans prediction remain the same) ... + kmeans = KMeans( + n_clusters=n_clusters, random_state=42, n_init=10 + ) # n_init suppresses warning + cluster_labels = kmeans.fit_predict(reduced_embeddings) + + # Group texts by cluster + clustered_texts: Dict[int, List[str]] = {i: [] for i in range(n_clusters)} + for text, label in zip(texts, cluster_labels): + clustered_texts[label].append(text) + + # Build Markdown content + markdown_content = "# Clustered and Rewritten Text Document\n\n" + markdown_content += "This document groups text chunks based on semantic similarity. Topics and rewritten text (formatted in Markdown with LaTeX for equations) are generated by the Gemini API.\n\n" + + for i in range(n_clusters): + cluster_topic = f"Cluster {i+1}" # Default topic + rewritten_content = "(Failed to generate rewritten text for this cluster.)" # Default content on failure + + if clustered_texts[i]: + # ... (Combine chunks and limit context size as before) ... + combined_text = "\n\n---CHUNK SEPARATOR---\n\n".join(clustered_texts[i]) + context_limit = ( + 15000 # Adjust based on model context window and typical chunk size + ) + if len(combined_text) > context_limit: + print( + f"Warning: Combined text for cluster {i+1} exceeds {context_limit} chars, truncating for API call." + ) + combined_text = combined_text[:context_limit] + "..." + + # 1. Ask Gemini for a topic + # ... (Topic generation prompt and call remain the same) ... + topic_prompt = f"Analyze the following text excerpts separated by '---CHUNK SEPARATOR---'. Provide only a concise topic title (3-5 words maximum) that captures the main theme. Do not add any explanation or introductory text.\n\nText Excerpts:\n{combined_text}" + print(f" Generating topic for Cluster {i+1}...") + generated_topic = call_gemini_api(topic_prompt) + if generated_topic: + cluster_topic = ( + generated_topic.replace('"', "").replace("Topic:", "").strip() + ) + else: + print(f" Failed to generate topic for Cluster {i+1}.") + + # 2. Ask Gemini to rewrite the text with Markdown and LaTeX formatting + # Updated prompt with formatting instructions + rewrite_prompt = f"""Rewrite and reorder the following text chunks, separated by '---CHUNK SEPARATOR---', into a single, coherent, and grammatically correct text. +Preserve all the original information and meaning, but improve the flow and readability. +Format the entire output as Markdown. +Use LaTeX delimiters for all mathematical equations: '$' for inline equations (e.g., $E=mc^2$) and '$$' for multiline equations (e.g., $$a^2 + b^2 = c^2$$). +Do not add any commentary, introduction, or conclusion beyond the rewritten text itself. + +The output Language should be German besides technical terms. +You do not have to include citations for people or works mentioned in the text, unless they are essential to the meaning of the text. + +Text Chunks: +{combined_text}""" + print(f" Rewriting text for Cluster {i+1}...") + generated_rewrite = call_gemini_api(rewrite_prompt) + if generated_rewrite: + rewritten_content = generated_rewrite + else: + print( + f" Failed to rewrite text for Cluster {i+1}. Using original chunks." + ) + # Fallback to original chunks if rewrite fails + rewritten_content = ( + "**Original Chunks (Rewrite Failed):**\n\n" + + "\n\n---\n\n".join(clustered_texts[i]) + ) + + # Add to Markdown + markdown_content += f"## {cluster_topic}\n\n" + markdown_content += ( + f"{rewritten_content}\n\n" # Add the rewritten content (or fallback) + ) + + # Add a separator between clusters in the markdown file + if i < n_clusters - 1: + markdown_content += "\n---\n\n" + + # Write to Markdown file + try: + # Ensure the directory exists before writing (handled in main) + with open(output_filename, "w", encoding="utf-8") as f: + f.write(markdown_content) + print(f"Clustered and rewritten text saved to: {output_filename}") + except IOError as e: + print(f"Error writing Markdown file {output_filename}: {e}") + + print("\n--- End of Markdown Generation ---") + + +# --- Stage 8: Querying / Semantic Search --- +def find_similar_chunks( + query: str, + texts: List[str], + embeddings: np.ndarray, # Use original embeddings for similarity + model: SentenceTransformer, + top_n: int = 5, +) -> List[Tuple[str, float]]: + """Finds text chunks most similar to the query.""" + print(f"\nSearching for chunks similar to: '{query}'") + if embeddings.shape[0] == 0: + print("No embeddings available for search.") + return [] + + # Generate embedding for the query + query_embedding = model.encode([query]) # Pass query as a list + + # Calculate cosine similarities + # embeddings should be 2D (n_samples, n_features) + # query_embedding should be 2D (1, n_features) + similarities = cosine_similarity(query_embedding, embeddings)[ + 0 + ] # Get the first row + + # Get indices of top_n highest similarities + # If fewer results than top_n, take all available + num_results = min(top_n, len(similarities)) + if num_results <= 0: + return [] + + # Use argsort to get indices of sorted similarities (descending) + sorted_indices = np.argsort(similarities)[::-1] + top_indices = sorted_indices[:num_results] + + # Prepare results + results = [(texts[i], float(similarities[i])) for i in top_indices] + print(f"Found {len(results)} relevant chunks.") + return results + + +# --- Main Execution --- +if __name__ == "__main__": + pdf_file = "in/example.pdf" + # Define base output directory + base_output_dir = "output" + # Create timestamp string + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + # Create unique subdirectory path + output_subdir = os.path.join(base_output_dir, timestamp) + # Define the final markdown output file path + markdown_output_file = os.path.join(output_subdir, "clustered_rewritten_gemini.md") + + try: + # Create the output directories if they don't exist + os.makedirs(output_subdir, exist_ok=True) + print(f"Output will be saved in: {output_subdir}") + + # 1. Extract Text + full_text = extract_text_from_pdf(pdf_file) + + # 2. Chunk Text + text_chunks = chunk_text(full_text, chunk_size=400, chunk_overlap=40) + + if not text_chunks: + print("No text chunks generated. Exiting.") + exit() + + # 3. Generate Embeddings + original_chunks, embeddings_list = generate_embeddings(text_chunks) + + # 4. Simulate Storage & Retrieval + simulate_pgvector_storage(original_chunks, embeddings_list) + fetched_chunks, fetched_embeddings_list = simulate_fetch_from_db() + + if not fetched_embeddings_list: + print("No valid embeddings fetched. Cannot proceed. Exiting.") + exit() + + # Convert fetched list back to NumPy array + original_embeddings_np = np.array(fetched_embeddings_list) + + # 5. Apply t-SNE + num_chunks = len(fetched_chunks) + tsne_perplexity = min(30.0, max(5.0, num_chunks / 4.0)) + reduced_embeddings_3d = apply_tsne( + original_embeddings_np, + n_components=3, + perplexity=tsne_perplexity, + random_state=42, + verbose=1, + ) + + # 6. Visualize + if reduced_embeddings_3d.shape[0] > 0: + plot_embeddings_interactive(reduced_embeddings_3d, fetched_chunks) + else: + print("No reduced embeddings generated for plotting.") + + # 7. Cluster and Generate Markdown File using Gemini + if reduced_embeddings_3d.shape[0] > 0: + num_chunks = len(fetched_chunks) + num_clusters = min(8, max(2, num_chunks // 10)) + generate_clustered_markdown( # Call updated function + reduced_embeddings_3d, + fetched_chunks, + markdown_output_file, # Pass the full dynamic path + n_clusters=num_clusters, + ) + else: + print("No reduced embeddings available for clustering.") + + # 8. Interactive Querying Loop + print("\n--- Interactive Query Mode ---") + print("Enter a search query (or type 'quit' to exit):") + while True: + user_query = input("> ") + if user_query.lower() == "quit": + break + if not user_query.strip(): + continue + + # Perform search using original embeddings + search_results = find_similar_chunks( + user_query, + fetched_chunks, + original_embeddings_np, # Use full embeddings for search + embedding_model, + top_n=3, # Show top 3 results + ) + + if search_results: + print("--- Top Results ---") + for i, (text, score) in enumerate(search_results): + print(f"[{i+1}] Score: {score:.4f}\n Text: {text[:300]}...\n") + else: + print("No relevant chunks found.") + + except FileNotFoundError as e: + print(f"Error: {e}") + print("Please ensure 'example.pdf' exists in the same directory.") + except ImportError as e: + print(f"Import Error: {e}") + print("Please ensure all required libraries are installed:") + print( + "pip install pypdf sentence-transformers torch scikit-learn numpy plotly pandas google-generativeai" + ) + print("You might need: pip install scikit-learn google-generativeai") + except ValueError as e: # Catch missing API key error from helper + if "Missing GEMINI_API_KEY" in str(e): + # Message already printed in helper function, maybe add exit instruction + print("Please set the GEMINI_API_KEY environment variable and try again.") + exit(1) # Exit if API key is missing + else: + print(f"An unexpected value error occurred: {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") + # import traceback + # traceback.print_exc() diff --git a/Code/pipeline-example/output/20250406_170841/clustered_rewritten_gemini.md b/Code/pipeline-example/output/20250406_170841/clustered_rewritten_gemini.md new file mode 100644 index 0000000..dfb4461 --- /dev/null +++ b/Code/pipeline-example/output/20250406_170841/clustered_rewritten_gemini.md @@ -0,0 +1,364 @@ +# Clustered and Rewritten Text Document + +This document groups text chunks based on semantic similarity. Topics and rewritten text (formatted in Markdown with LaTeX for equations) are generated by the Gemini API. + +## Game Theory Basics +Non-Cooperative Games +Game Strategies +Prisoner's Dilemma +Zero-Sum Games +Conservative Strategies +Mixed Strategies +Checkpoints Summary +Group Decisions +Collective Choice + +# Spieltheorie Zusammenfassung + +R.-P. Mundani, ICS, HS 2024 + +Non-cooperative games are widely used in literature and applications. Players cannot make agreements before the game, and they have no agreements on joint actions that are optimal for the group. A second distinction is between simultaneous games, where all players make their choice once and for all (only one choice). + +R.-P. Mundani, ICS, HS 2024 + +**Spielen in reinen Strategien:** A dominant strategy is defined as follows: For two strategies $a_i, a_i^′ ∈ A_i$, strategy $a_i$ is weakly dominant over strategy $a_i^′$ if $a_i$ is at least as good as $a_i^′$ for all possible actions of the other players $a_j ∈ A_j≠i$, i.e., for all $a_j ∈ A_j≠i$. If the inequality holds strictly ($>$), then we speak of strong dominance. A player can achieve a higher payoff by deviating from this, i.e., if it holds for all $a_i ∈ A_i$ and $i ∈ N$. What does this mean for the prisoner's dilemma…? + + +R.-P. Mundani, ICS, HS 2024 + +**Spielen in reinen Strategien:** Nash equilibrium: For action profile (G, G) + +| | S1 | S2 | +| :---- | :---- | :---- | +| **G** | (6, 6) | (0, 8) | +| **L** | (3, 3) | (8, 0) | + +The dominant strategy for both players is G. HOWEVER: The equilibrium (G, G) is inefficient, since (L, L) would yield a better payoff. + +R.-P. Mundani, ICS, HS 2024 + +**Weiteres Beispiel: Falke-und-Taube-Spiel:** Two competitors must decide between aggressive (Hawk) and non-aggressive (Dove) behavior. + + +R.-P. Mundani, ICS, HS 2024 + +**Zwei-Personen-Nullsummenspiele:** Introduction: Players try to maximize their profit (i.e., minimize the profit of the other). The sum is always zero for all players. 2PNS games are usually represented in normal form (or matrix form). + +| | S21 | S22 | +| :---- | :---- | :---- | +| **S11** | (-1, 1) | (-5, 5) | +| **S12** | (1, -1) | (-3, 3) | + +Matrix form: $u_2$ or $-u_1$ + + +R.-P. Mundani, ICS, HS 2024 + +**Zwei-Personen-Nullsummenspiele:** Definition: Conservative Strategies. Observation: The best opponent strategy leads to one's own worst profit. Conservative strategy: Choose the best value from all unfavorable cases. Minimizer P1 (→ intersection of minimum and maximum). The idea remains the same: no player can improve themselves through a mutual change of strategy. + + +R.-P. Mundani, ICS, HS 2024 + +**Zwei-Personen-Nullsummenspiele:** Example: Battle of the Bismarck Sea (continuation). If the Americans check the correct route, the bombing can begin immediately (otherwise two days remain for the other route). On the northern route, the bombing must also be omitted for one day due to poor visibility. This results in the following 2PNS game (profit ≡ possible days for bombing). + +| | N | S | +| :---- | :---- | :---- | +| **N** | (2, 2) | (1, 3) | +| **S** | (3, 1) | (0, 0) | + +$i^* = N$ and $U^- = 2$; $j^* = N$ and $U^+ = 2$; Saddle point at (N, N). + + +R.-P. Mundani, HS 2024 + +**Spielen in gemischten Strategien:** Preliminary remarks: Pure strategies: A player commits (a priori) to a strategy. Disadvantage: Predictability by fellow players. + + +R.-P. Mundani, ICS, HS 2024 + +**Spielen in gemischten Strategien:** Finding the saddle point: Multiply the 2PNS game by probabilities $p$ and $q$. Solving the corresponding equation (often by linear programming). + +R.-P. Mundani, ICS, HS 2024 + +| | S21 | S22 | +| :---- | :---- | :---- | +| **S11** | 3 | -1 | +| **S12** | 1 | 5 | + +$p = 0.5$ and $q = 0.25$ (e.g., with gnuplot (http://www.gnuplot.info) via $> f(x,y) = -8*x*y + 2*x + ...$) + + +R.-P. Mundani, ICS, HS 2024 + +**Checkpoints:** What terms should be clear…? Games in pure strategies; Representation in normal form/bimatrix form; Dominant strategy; Nash equilibrium; Two-person zero-sum games; Higher penalties (fines) equally reduce the probability with which the tax inspector checks and with which tax evasion becomes attractive for Zanoma  + + +R.-P. Mundani, ICS, HS 2024 + +**Gruppenentscheidungen:** Decision: Preference $P_A$. Sought: Collective selection function $K$ for the preference of the totality $K: P_A = P_A × P_A × … × P_A → P_A$. Essential (non-trivial) conditions: The selection function $K$ must be total → for each possible combination of (personal) preferences from $P_A$ there must be an overall preference (i.e., a result). The result itself must again be a relation in $P_A$. + + +R.-P. Mundani, ICS, HS 2024 + +**Gruppenentscheidungen:** Decision procedure (continuation): Rank addition. Each individual preference $ρ_i$ defines a rank mapping. Idea: Determine the collective preference from the rank numbers of the respective individuals. Simplest method: Addition of rank numbers. For two elements $x, y$, $K_A(ρ_1, …, ρ_n)$ is relation $ρ$ with: The sum $Σr_i$ is not a rank mapping (see following example), but can easily be “corrected” so that $ρ ∈ P_A$ applies. The procedure has no obvious disadvantages. + + +R.-P. Mundani, ICS, HS 2024 + +**Gruppenentscheidungen:** Collective selection functions $P_A → P_A$ are also fulfilled by undesirable procedures. Two conditions that every “fair” procedure should fulfill: Pareto condition: The totality (of all individuals) can enforce any desired ranking for any choice options by unanimity tests. Independence of irrelevant alternatives: Ranking between two choice options is determined by preference $ρ_d$; the result is independent of the other $ρ_i$ with $i ≠ d$. The result is also a mapping $P_A → P_A$, but neither “fair” nor “democratic”. Better procedures are needed that “reasonably” collectively combine individual preferences. + + +--- + +## Game Theory Basics +Two-Person Zero-Sum Games +Bismarck Sea Battle +Mixed Strategies Game +Group Decisions +Preference Relations +Decision Procedures +Arrow's Impossibility Theorem + +# Spieltheorie: Von Zwei-Personen-Nullsummenspielen zu Gruppenentscheidungen + +Zwei-Personen-Nullsummenspiele (2PNS-Spiele) werden in Matrixform dargestellt, wobei Spieler 1 die Minimiererin (Zeilenspielerin) und Spieler 2 die Maximiererin (Spaltenspielerin) ist. Daher werden 2PNS-Spiele auch als MinMax-Spiele bezeichnet. Aufgrund ihrer speziellen Struktur nehmen Nash-Gleichgewichte die Form von Sattelpunkten an – dem Schnittpunkt von Minimum und Maximum. Der Min-Max-Theorem besagt, dass ein Sattelpunkt genau dann existiert, wenn $U^- = a_{i*j*} = U^+$. Ein Beispiel hierfür ist die Schlacht in der Bismarcksee (2.–4. März 1943), wo japanische Truppen von Rabaul nach Lae verlegt werden sollten, mit zwei möglichen Routen (regnerische Nordroute und sonnige Südroute), während die U.S. Air Force mit begrenzten Ressourcen entscheiden musste, welche Route zu bombardieren ist. + +Möglichkeiten und Aktionen seien definiert als $A_1 = \{X, Y\}$ und $A_2 = \{X, Y\}$. Die Aktionsprofile sind $A = \{(X, X), (X, Y), (Y, X), (Y, Y)\}$. Eine Auszahlungsfunktion $u_{1,2}: A \to (1, 0, 0, 1)$ ist gegeben. Die Frage ist, wie sich das Spiel verändert, wenn die Auszahlungsfunktionen für Spieler 1: $u_1: A \to (1, 0, 0, 0)$ und Spieler 2: $u_2: A \to (0, 0, 0, 1)$ gelten. Im Kontext von Spielen in gemischten Strategien, beispielsweise bei der Steuerhinterziehung mit einer Schwelle von $s > 200$ Millionen CHF, kann es vorkommen, dass keine dominante Strategie oder kein Nash-Gleichgewicht in reinen Strategien existiert. In einem Beispiel ergibt sich $U^- = 2 = U^+$ aus $q$ in $S_{11}$ oder $S_{12}$ und $p$ in $S_{22}$. + +Gruppenentscheidungen beschäftigen sich mit Relationen, die Beziehungen zwischen Elementen beschreiben (z.B. Rangfolgen, Präferenzen). Eine Relation $R$ auf Paaren $(x, y)$ von Elementen aus $A$ wird formal als $(x, y) \in R$ oder $xRy$ geschrieben. Präferenzen können durch Rangabbildungen definiert werden: $x \rho y \iff r(x) < r(y)$. Die Relation $\rho$ ist transitiv und asymmetrisch. Die Menge aller darstellbaren Relationen auf $A$ ist $P_A := \{\rho \subset A \times A: \rho \text{ erfüllt } x \rho y \iff r(x) < r(y) \text{ für eine Rangabbildung } r\}$. Eine Erweiterung der Relation durch alle Paare mit gleichem Rang ist $x \rho^* y \iff r(x) \le r(y)$. Die Relation $\rho^*$ ist transitiv und reflexiv, und es gilt $\rho \subset \rho^*$. $P_A^* := \{\rho^* \subset A \times A: \rho^* \text{ erfüllt } x \rho^* y \iff r(x) \le r(y) \text{ für eine Rangabbildung } r\}$. Beispiel: $A = \{x, y, z\}$, $r(x) = 1$, $r(y) = r(z) = 2$. Es gilt $x \rho y \iff \neg(y \rho^* x)$. + +Entscheidungsverfahren für Gruppenentscheidungen umfassen Extrembeispiele wie den externen oder internen Diktator. Rangaddition ist ein weiteres Verfahren, aber es kann zu ungültigen Ergebnissen führen, wie z.B. $x \rho y$, $y \rho z$, und $z \rho x$, was aufgrund der Transitivität zu $x \rho x$ führt, was ungültig ist. Einstimmigkeit erfordert, dass alle Individuen dieselbe Präferenz teilen. Das Condorcet-Verfahren und Einstimmigkeit liefern für $|A| > 2 möglicherweise ungültige Ergebnisse. Nur ein interner Diktator erfüllt alle Bedingungen. Ein Verfahren sollte die Unabhängigkeit irrelevanter Alternativen erfüllen: die Rangfolge zwischen zwei Wahlmöglichkeiten sollte nicht durch Präferenzänderungen der Individuen bezüglich einer dritten Wahlmöglichkeit beeinflusst werden. Der Satz von Arrow zeigt, dass mit diesen Forderungen kein annähernd demokratisches Verfahren möglich ist. + + +--- + +## Game Theory Introduction + +# Introduction to Computational Science (CDS-1012) HS 2024 Prof. Dr. rer. nat. habil. Ralf-Peter Mundani DAViS + +R.-P. Mundani, ICS, HS 2024 + +**Introduction to Game Theory** + +* Overview +* Modeling and Definitions +* Games in Pure Strategies +* Nash Equilibrium and Dominant Strategies +* Two-Person Zero-Sum Games +* Conservative Strategies +* Games in Mixed Strategies +* Calculation of Saddle Points +* Majority Decisions / Group Decisions (→ Part 2) + +**Motivation from Stochastics: The Goat Problem (a.k.a. Monty-Hall Dilemma)** + +* Game show: 3 doors – two goats and one car +* Player chooses a door, the showmaster opens another door (goat)  Strategy: Change choice…? +* The other two cases (player chooses door B or C) are symmetrical +* In three cases (3/9) loss, in six cases (6/9) win  Change choice  + +**Fundamentals** + +* Definition: Game theory +* Subfield of mathematics ("Theory of strategic thinking") +* Modeling of decision situations + +**Games in Pure Strategies** + +* Prisoner's Dilemma (the best-known non-cooperative game in pure strategies) +* Background: Two prisoners are accused of having committed a crime together. Both prisoners are interrogated separately and cannot communicate. Due to a lack of concrete evidence, only part of the crime can be proven against both. +* Possible actions: Deny (L): low penalty (2 years); Confess (G): high penalty (5 years) or maximum penalty (8 years → witness protection program) +* Maximizing the payoff: Function $u_i$ models the gain in freedom (i.e., maximum penalty – actual penalty) + +**Prisoner's Dilemma (continuation)** + +* Representation in normal form (also called bimatrix form for two players) +* Question: How do both players find the best strategy for themselves…? + +$ +\begin{array}{c|cc} + & G & L \\ +\hline +G & (6, 6) & (8, 0) \\ +L & (0, 8) & (3, 3) \\ +\end{array} +$ +$A_2 A_1 (u_1, u_2)$ + +**A Little Bit of Math ** + +* Nash Equilibrium +* An action profile is a Nash equilibrium if no player can improve their payoff by unilaterally changing their action. +* If strategy $a_i$ is better than strategy $a_i$ in all cases, then strong dominance is spoken of, i.e., strategy $a_i$ is strongly dominant over strategy $a_i$. +* Profiles of dominant strategies lead to Nash equilibria (converse not valid). +* Again the question: What does this mean for the prisoner's dilemma…? + +**Strong Dominant Strategy** + +* The strongly dominant strategy for both players is G. + +**Exercise: Tax Evasion** + +* Zanoma, a company, has not paid taxes on 200M CHF. +* If Zanoma is proven to have evaded taxes by a private tax auditor, the company must pay s million CHF (tax plus fines). +* The tax auditor receives 5% of the repayment as a bonus in case of success. +* The tax auditor has strategies audit (P; costs: 10M CHF) and no audit (KP; costs: 0). +* Zanoma has strategies tax evasion (S) and no tax evasion (KS). +* Questions: a) What does the depicted situation look like as a game in bimatrix form…? b) Are there dominant strategies and Nash equilibria for $s < 200$M CHF…? + +**Games in Pure Strategies** + +* Sequential games +* There is a specific order in which player(s) are allowed to decide. +* In each step, players who are allowed to decide have complete or incomplete knowledge of the current status (i.e., previous decisions). +* The usual form of representation is extensive or as a tree. + +**Conservative Strategies** + +* Players choose strategies to minimize losses in the worst-case scenario. +* Minimizer P1: $U^- = \text{minimax}_{j} a_{ij}$ +* For each strategy $i$: maximize over all columns $j$ (⇒ largest possible loss) +* Choose the minimum of all column maxima → conservative strategy $i^*$ (upper bound $U^-$) +* Maximizer P2: $U^+ = \text{max}_j \text{min}_i a_{ij}$ +* For each strategy $j$: minimize over all rows $i$ (⇒ smallest possible gain) +* Choose the maximum of all row minima → conservative strategy $j^*$ (lower bound $U^+$) +* Mixed strategy games always have a saddle point. + +**Games in Mixed Strategies** + +* Introduction of probabilities +* Player P1: plays strategy $S_{11}$ with probability $p$ and strategy $S_{12}$ with $1 - p$. +* Player P2: plays strategy $S_{21}$ with probability $q$ and strategy $S_{22}$ with $1 - q$. +* For the case $n > 2$ strategies: probabilities $p_1, p_2, \dots, p_n$ for each strategy. +* Determining the saddle point +* Multiplying the 2PNS game + + +$ +\begin{array}{cc} +p & 1-p \\ +q & 1-q +\end{array} +$ + +**Tax Evasion (continuation)** + +* From Zanoma's perspective: $(200 - s) \cdot q + 200 \cdot (1 - q) = 0 \cdot q + 0 \cdot (1 - q) \iff q = 200/s$ +* From the tax auditor's perspective: $(0.05 \cdot s - 10) \cdot p - 10 \cdot (1 - p) = 0 \cdot p + 0 \cdot (1 - p) \iff p = 200/s$ +* i.e., Nash equilibrium in mixed strategies, thus for $p = q$, i.e., for $(200/s, 200/s)$ +* In other words: higher repayments (fines) reduce the probability of an audit. + +**Group Decisions** + +* Arrow's theorem +* Let $A$ with $|A| > 2$ be a set of choices. +* $K: P^A \to P^A$ is a collective choice function that satisfies the Pareto condition and independence of irrelevant alternatives. +* Then there is always a dictator, there is a $d \in I$: for all $(\rho_1, \dots, \rho_n) \in P^A$: for all $(x, y) \in A \times A$: $x \rho_d y \implies x \rho y$. +* BUT: The dictator is "generous" towards other groups. + + +--- + +## Game Theory Basics + +Kooperative Spiele zeichnen sich dadurch aus, dass alle Spielerinnen und Spieler nach gemeinsamen Aktionen suchen, die optimal für die Gruppe sind. Spielerinnen und Spieler können vor dem Spiel Absprachen treffen. Im Gegensatz dazu stehen nicht-kooperative Spiele, bei denen jede Spielerin und jeder Spieler seine Auszahlung maximiert, indem er seine beste Strategie wählt – basierend auf seinem Wissen über die Strategien der anderen. Vereinbarungen zwischen den Spielerinnen und Spielern gibt es in nicht-kooperativen Spielen nicht. + + +--- + +## Game Theory Strategies + +Mixed Strategies + +Group Decisions Ranking + +Group Decision Preferences + +Group Decision Methods + +# Gruppenentscheidungen: Präferenzen und Entscheidungsverfahren + +In Spielen können reine Strategien (z.B. Schere (100%), Stein (0%), Papier (0%)) zu vorhersehbaren Ergebnissen führen. Gemischte Strategien, bei denen ein Spieler zufällig zwischen Strategien entscheidet (z.B. Schere (25%), Stein (50%), Papier (25%)), werden durch eine Wahrscheinlichkeitsfunktion über reinen Strategien charakterisiert. Allerdings führen reine Strategien oft nicht zu Nash-Gleichgewichten. Das Min-Max-Theorem besagt, dass Zwei-Personen-Nullsummen-Spiele (2PNS-Spiele) in gemischten Strategien immer einen Sattelpunkt haben, im Gegensatz zu Spielen in reinen Strategien, die keinen Sattelpunkt besitzen (Beispiel: siehe Spielmatrix). + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & 3 & -1 \\ +S_{12} & -1 & 5 +\end{array} +$$ + +$\min_i \max_j \to i^* = S_{11} \text{ und } U^+ = 3$; $\max_j \min_i \to j^* = S_{22} \text{ und } U^- = 1$ + + +Rangabbildungen bieten eine Möglichkeit, Präferenzen zu visualisieren. Eine endliche Menge $A$ von Möglichkeiten (z.B. Kandidaten, Pläne) wird durch die Vergabe von Rangnummern $r(x)$ mit $x \in A$ bewertet. $r(x) < r(y)$ bedeutet, dass $x$ gegenüber $y$ bevorzugt wird. Zwei oder mehr Möglichkeiten können dieselbe Rangnummer erhalten. Eine Rangabbildung $r: A \to P$ ist eine surjektive Abbildung der Menge der Möglichkeiten $A$ auf eine Menge von Präferenzen $P = \{1, \dots, k\} \subset \mathbb{N}$. Beispiel: $A = \{x, y, z\}$, $r(x) = 1$, $r(y) = r(z) = 2$. Rangabbildungen eignen sich besonders für Visualisierungen (z.B. Fussballtabellen). + + +Präferenzen können durch Relationen beschrieben werden. Eine Relation $R$ auf einer Menge $A$ ist eine Teilmenge von $A \times A$. $xRy$ bedeutet, dass $x$ in Relation zu $y$ steht. Eigenschaften von Relationen: transitiv ($xRy \land yRz \implies xRz$), reflexiv ($xRx$ für alle $x \in A$), asymmetrisch ($xRy \land yRx$ gilt niemals gleichzeitig). Transitive und reflexive Relationen werden auch Quasiordnungen genannt. Eine strenge Präferenzrelation $\rho$ ist irreflexiv ($x \rho x$ gilt niemals) und asymmetrisch ($x \rho y \iff \neg(y \rho^* x)$). + +Entscheidungsverfahren benötigen die Menge der Wähler $I = \{1, \dots, n\}$ und die persönlichen Präferenzen $P_A$ jedes Wählers. Ziel ist die kollektive Auswahlfunktion. Das Condorcet-Verfahren zählt im direkten Vergleich zweier Elemente $x, y \in A$, welche Präferenz häufiger auftritt. Für zwei Elemente $x, y$ gilt: $K(ρ_1, \dots, ρ_n)$ ist eine Relation $ρ$. Das Verfahren ist für beliebige $ρ$ durchführbar, aber die Relation $ρ$ ist im Fall von mehr als zwei Möglichkeiten nicht immer transitiv, d.h. $ρ$ ist keine zulässige Präferenzrelation aus $P_A$. + +Bedingungen an Auswahlverfahren: Die Pareto-Bedingung (Einstimmigkeit) besagt, dass die kollektive Auswahlfunktion $K: P_A \to P_A$ die Pareto-Bedingung erfüllt, wenn für alle $ρ_i \in P_A$ gilt: Wenn alle Individuen $x$ vor $y$ bevorzugen, dann muss die kollektive Entscheidung auch $x$ vor $y$ bevorzugen. Die Unabhängigkeit von irrelevanten Alternativen fordert, dass die Platzierung einer dritten Möglichkeit den Vergleich zweier anderer Möglichkeiten nicht beeinflussen darf. Präferenzänderungen bei Individuen können zu unterschiedlichen kollektiven Entscheidungen führen. Beispiel: Zwei Individuen mit unterschiedlichen Präferenzen ($i=1$: $x < y < z$; $i=2$: $y < z < x$) führen zu unterschiedlichen Ergebnissen. + + +--- + +## Game Theory Basics + +# Einführung in die Spieltheorie + +**Grundlagen (Fortsetzung):** Entscheidungen können getroffen werden unter: + +* **Gewissheit:** Alle Aktionen/Konsequenzen sind bekannt. +* **Risiko:** (Bestimmte) Wahrscheinlichkeiten von Aktionen/Konsequenzen sind bekannt. +* **Unsicherheit:** Keine Aktionen/Konsequenzen sind bekannt. + +**Modellierung:** + +* Menge von SpielerInnen $N = \{1, 2, \dots, n\}$ +* Menge der Aktionen (auch als Strategie bezeichnet) $A_i$ von SpielerIn $i$ für alle $i \in N$ +* Menge der Aktionsprofile (auch Strategieprofile) $A = \{(a_i)_{i \in N}, a_i \in A_i \text{ für alle } i \in N\}$ +* Auszahlungsfunktion $u_i: A \to \mathbb{R}$ für SpielerIn $i$ + +**Beispiel:** Entscheidung zwischen zwei Möglichkeiten. Aktionen: $A_1 = \{X, Y\}$ und ... + +**Nash-Gleichgewicht:** Ein Aktionsprofil bildet ein Nash-Gleichgewicht, wenn keine SpielerIn eine höhere Auszahlung erhalten kann, indem sie von ihrer Entscheidung (einseitig) abweicht, d.h. während alle anderen SpielerInnen auf ihren Entscheidungen beharren. Formal: Ein Aktionsprofil bildet genau dann ein Nash-Gleichgewicht, wenn keine der SpielerInnen durch Abweichung davon eine höhere Auszahlung erhält. + +**Modellierung von Entscheidungssituationen (oftmals im sozialen Konflikt):** Idee: Erfolg hängt nicht nur von eigener, sondern auch von Entscheidungen anderer ab. Anwendungsgebiete: vor allem Wirtschaftswissenschaften und Ökonomie. Bekannte Vertreter: John von Neumann, John Forbes Nash. Unterscheidung zwischen: + +* kooperativen / nicht-kooperativen Spielen +* simultanen / sequentiellen Spielen + +**Spielen in reinen Strategien:** Übung: Steuerhinterziehung. Unternehmen Zanoma hat durch kreative Buchführung 200M CHF nicht versteuert. + +**Spielen in reinen Strategien (Fortsetzung):** Falls $s < 200$ folgt damit $200 - s > 0 \to S$ ist strikt dominante Strategie. Falls $s < 200$ folgt damit $0.05 * s - 10 < 0 \to KP$ ist strikt dominante Strategie. + +\begin{array}{c|cc} + & KP & S \\ +\hline +K & (200, 0) & (200 - s, 0.05 * s - 10) \\ +S & (0, 0) & (0, -10) \\ +\end{array} + +**Zwei-Personen-Nullsummenspiele:** Einführung. Idee: Gewinn einer SpielerIn ist Verlust der anderen (d.h. Summe ist immer 0). Alternative Berechnung (als reine Strategien) aus jeweiliger Sicht von SpielerIn P1 und P2. + +$f(x, y) = -8xy + 2x + 4y + 1$ + +**Spielen in gemischten Strategien:** Sattelpunkt bei (0.5, 0.25); durch Einsetzen von $p$ in $S_{21}$ oder $S_{22}$ sowie $q$ in $S_{11}$ oder $S_{12}$ ergibt sich ... + +\begin{array}{c|cc} + & S_{11} & S_{12} \\ +\hline +S_{21} & 1-q & q \\ +S_{22} & 3-1 & 1-p \\ +\end{array} +$P_1(\min)$ $P_2(\max)$ + +**Zwei-Personen-Nullsummenspiele:** Darstellung in Matrixform. Konservative Strategien (MinimiererIn / MaximiererIn). Spiele in gemischten Strategien. Gleichgewichtspunkte / Sattelpunkt. + + +**Gruppenentscheidungen:** Problemstellung. Situation: verschiedene Möglichkeiten (Entscheidungsvarianten: Wahlen, Wettbewerbe, ...). Ziel: gemeinsame Bestimmung einer Rangfolge der Möglichkeiten. Beispiel: WählerInnen $\to$ KandidatInnen (Wahl), Publikum $\to$ TeilnehmerInnen (Wettbewerb). ABER: nicht alle werden mit der Rangfolge einverstanden sein (Stichwort: Unzufriedenheit). Axiomatischer Ansatz. Aufstellung von Eigenschaften und Prüfung, welche Entscheidungsverfahren sie erfüllen. Modellierung individueller Präferenzen und der Entscheidungsverfahren selbst. + +**Gruppenentscheidungen (Fortsetzung):** Bedingungen an Auswahlverfahren. Unabhängigkeit von irrelevanten Alternativen. Beispiel: $I = \{1, 2\}$, $A = \{x, y, z\}$ mit Rangaddition KA als Auswahlverfahren. Als Gruppenentscheidung bzgl. $x$ und $y$ gilt $y \rho' x$, aber nicht $x \rho y$. Ergebnis ändert sich, obwohl kein Individuum die Präferenz im direkten Vergleich zwischen $x$ und $y$ ändert ($i = 1$ sieht $x < y$ und $i = 2$ sieht $y < x$). + diff --git a/Code/pipeline-example/output/20250406_171517/clustered_rewritten_gemini.md b/Code/pipeline-example/output/20250406_171517/clustered_rewritten_gemini.md new file mode 100644 index 0000000..0571645 --- /dev/null +++ b/Code/pipeline-example/output/20250406_171517/clustered_rewritten_gemini.md @@ -0,0 +1,213 @@ +# Clustered and Rewritten Text Document + +This document groups text chunks based on semantic similarity. Topics and rewritten text (formatted in Markdown with LaTeX for equations) are generated by the Gemini API. + +## Game Theory Basics +Nash Equilibrium +Dominant Strategies +Prisoner's Dilemma +Zero-Sum Games +Conservative Strategies +Mixed Strategies +Checkpoints Summary +Group Decisions +Collective Choice + +# Spieltheorie: Zusammenfassung + +Nicht-kooperative Spiele sind in der Literatur und Anwendung weit verbreitet. Spieler*innen können keine Absprachen vor dem Spiel treffen und haben keinerlei Vereinbarungen über gemeinsame Aktionen, die optimal für die Gruppe sind. Eine Unterscheidung ist die zwischen simultanen Spielen, bei denen alle Spieler*innen ihre Wahl ein für alle Mal treffen (nur ein Zug), und sequentiellen Spielen. + +Spielen in reinen Strategien: Eine dominante Strategie ist mindestens so gut wie eine andere Strategie für alle möglichen Aktionen der anderen Spieler*innen. Gilt die Ungleichung im strengen Sinn ($>$), spricht man von starker Dominanz. Ein Nash-Gleichgewicht liegt vor, wenn kein Spieler durch eine einseitige Abweichung eine höhere Auszahlung erzielen kann, d.h. wenn für alle $a_i \in A_i$ und $i \in N$ gilt. Das Gefangenendilemma illustriert dies: Die dominante Strategie beider Spieler*innen ist G. ABER: Das Gleichgewicht (G, G) ist ineffizient, da (L, L) eine bessere Auszahlung ergeben würde. Ein weiteres Beispiel ist das Falke-und-Taube-Spiel. + +$$ +\begin{array}{c|cc} + & G & L \\ +\hline +G & (6, 6) & (8, 0) \\ +L & (0, 8) & (3, 3) \\ +\end{array} +$$ + +Zwei-Personen-Nullsummenspiele: Spieler*innen versuchen, den Gewinn zu maximieren (d.h. den Gewinn der anderen zu minimieren); der Verlust des einen ist der Gewinn des anderen (d.h. die Summe ist immer Null). Die Darstellung erfolgt üblicherweise in Normalform (oder Matrixform). Konservative Strategien wählen den besten Wert aus allen ungünstigsten Fällen. Die beste gegnerische Strategie führt zum eigenen schlechtesten Gewinn. Der Sattelpunkt (Schnittpunkt von Minimum und Maximum) zeigt die optimale Strategie. Keine Spieler*in kann sich durch einen einseitigen Strategiewechsel verbessern. + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & (-1, 1) & (-5, 5) \\ +S_{12} & (1, -1) & (3, -3) \\ +\end{array} +$$ + +Beispiel: Schlacht in der Bismarcksee. Falls die Amerikaner die richtige Route überprüfen, kann die Bombardierung sofort begonnen werden (andernfalls verbleiben zwei Tage für die andere Route). Auf der Nordroute muss die Bombardierung wegen schlechter Sicht einen Tag ausbleiben. Dies ergibt folgendes 2PNS-Spiel (Gewinn ≡ mögliche Tage für Bombardierung): + +$$ +\begin{array}{c|cc} + & N & S \\ +\hline +N & (2, -2) & (1, -1) \\ +S & (0, 0) & (2, -2) \\ +\end{array} +$$ + +Spielen in gemischten Strategien: Reine Strategien sind vorhersehbar. Ein Sattelpunkt im 2PNS-Spiel wird durch Multiplikation mit Wahrscheinlichkeiten $p$ und $q$ und Lösung der zugehörigen Gleichung (oftmals durch lineare Programmierung) gefunden. + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & 3 & -1 \\ +S_{12} & 1 & 5 \\ +\end{array} +$$ + +Checkpoints: Spiele in reinen Strategien, Darstellung in Normalform/Bimatrixform, dominante Strategie, Nash-Gleichgewicht, Zwei-Personen-Nullsummenspiele. + +Gruppenentscheidungen: Gesucht ist eine kollektive Auswahlfunktion K für die Präferenz der Gesamtheit: $K: PA = PA \times PA \times \dots \times PA \to PA$. Eine Auswahlfunktion K muss total sein. Das Ergebnis muss eine Relation in PA sein. Rangaddition: Jede Individualpräferenz $\rho_i$ legt eine Rangabbildung fest. Die kollektive Präferenz wird aus den Rangnummern der jeweiligen Individuen bestimmt. Die Summe der Rangnummern ist keine Rangabbildung, kann aber korrigiert werden. Das Verfahren hat keine offensichtlichen Nachteile. Kollektive Auswahlfunktionen $PA \to PA$ werden auch von unerwünschten Verfahren erfüllt. Zwei Bedingungen für „gerechte“ Verfahren sind die Pareto-Bedingung und die Unabhängigkeit von irrelevanten Alternativen. + + +--- + +## Game Theory Basics +Two-Person Zero-Sum Games +Bismarck Sea Battle +Mixed Strategies Game +Group Decisions +Preference Relations +Decision Procedures +Arrow's Impossibility Theorem + +# Spieltheorie und Gruppenentscheidungen + +Zwei-Personen-Nullsummenspiele (2PNS-Spiele) werden auch als MinMax-Spiele bezeichnet. In Matrixform wird Spieler 1 zur Minimiererin (auch Zeilenspielerin genannt) und Spieler 2 zur Maximiererin (auch Spaltenspielerin genannt). Aufgrund der speziellen Struktur von 2PNS-Spielen nehmen Nash-Gleichgewichte die Form von Sattelpunkten an (Schnittpunkt von Minimum und Maximum). Der Sattelpunkt existiert genau dann, wenn $U^- = a_{i*j*} = U^+$. Ein Beispiel hierfür ist die Schlacht in der Bismarcksee (2.-4. März 1943). Japanische Truppen sollten von Rabaul nach Lae verlegt werden, wobei zwei mögliche Routen zur Verfügung standen: eine regnerische Nordroute und eine sonnige Südroute. Die US Air Force musste entscheiden, welche Route sie bombardiert, da nicht genügend Flugzeuge für beide Routen verfügbar waren. + +Möglichkeiten und Aktionen seien $A_1 = \{X, Y\}$ und $A_2 = \{X, Y\}$. Die Aktionsprofile sind $A = \{(X, X), (X, Y), (Y, X), (Y, Y)\}$. Die Auszahlungsfunktion ist $u_{1,2}: A \to (1, 0, 0, 1)$. Wie verändert sich das Spiel, wenn folgende Auszahlungsfunktionen gelten? Spieler 1: $u_1: A \to (1, 0, 0, 0)$; Spieler 2: $u_2: A \to (0, 0, 0, 1)$. Spiele in gemischten Strategien werden betrachtet, wenn keine dominante Strategie oder kein Nash-Gleichgewicht in reinen Strategien existiert. Beispielsweise bei der Steuerhinterziehung mit $s > 200$ Millionen CHF. + +Gruppenentscheidungen basieren auf der Beschreibung von Beziehungen (z.B. Rangfolgen, Präferenzen) zwischen Elementen. Dies wird als Relation $R$ auf Paaren $(x, y)$ von Elementen aus $A$ dargestellt, formal geschrieben als $(x, y) \in R$ oder abkürzend als $xRy$. Beispielsweise ist $R$ mit '$<$' (kleiner) und $xRy$ gleichbedeutend mit $x < y$. Präferenzrelationen werden durch Rangabbildungen definiert: $x \rho y \iff r(x) < r(y)$. Die Relation $\rho$ ist transitiv und asymmetrisch. Die Menge aller möglichen Relationen auf $A$ ist definiert als $P_A := \{\rho \subset A \times A: \rho \text{ erfüllt } x \rho y \iff r(x) < r(y) \text{ für eine Rangabbildung } r\}$. Eine Erweiterung der Relation durch alle Paare mit gleichem Rang ist $x \rho^* y \iff r(x) \le r(y)$. Die Relation $\rho^*$ ist transitiv und reflexiv. Es gilt $\rho \subset \rho^*$. Analog lässt sich $P_A^* := \{\rho^* \subset A \times A: \rho^* \text{ erfüllt } x \rho^* y \iff r(x) \le r(y) \text{ für eine Rangabbildung } r\}$ definieren. Beispiel: $A = \{x, y, z\}$, $r(x) = 1$, $r(y) = r(z) = 2$. Zwischen beiden Relationen gilt: $x \rho y \iff \neg(y \rho^* x)$. + +Entscheidungsverfahren in Gruppen werden betrachtet. Extrembeispiel 1: externer Diktator. Das Ergebnis ist unabhängig von den individuellen Präferenzen. Extrembeispiel 2: interner Diktator. Die Präferenz eines Individuums bestimmt das Ergebnis. Rangaddition ist ein weiteres Verfahren. Beispiel: $I = \{1, 2\}$, $A = \{x, y, z\}$. Einstimmigkeit erfordert, dass alle Individuen dieselbe Präferenz teilen. Jedoch kann die Rangfolge zwischen zwei Wahlmöglichkeiten durch Präferenzänderungen der Individuen im Hinblick auf eine dritte Wahlmöglichkeit gekippt werden. Der Satz von Arrow besagt, dass mit diesen Forderungen kein annähernd demokratisches Verfahren mehr möglich ist. Bedingungen an Auswahlverfahren: die Rangfolge zwischen zwei Wahlmöglichkeiten soll keinen Einfluss auf das Ergebnis haben. Rangaddition ist somit als „gerechtes“ Verfahren ausgeschlossen. Condorcet-Verfahren und Einstimmigkeit liefern für $|A| > 2$ evtl. ungültige Ergebnisse. Nur ein interner Diktator erfüllt alle Bedingungen. + + +--- + +## Game Theory Introduction + +# Einführung in die Spieltheorie (CDS-1012) HS 2024 Prof. Dr. rer. nat. habil. Ralf-Peter Mundani DAViS + +R.-P. Mundani, ICS, HS 2024 Einleitung: Überblick, Einführung in die Spieltheorie, Modellierung und Definitionen, Spiele in reinen Strategien, Nash-Gleichgewicht und dominante Strategien, Zwei-Personen-Nullsummenspiele, konservative Strategien, Spiele in gemischten Strategien, Berechnung von Sattelpunkten, Mehrheitsbeschlüsse / Gruppenentscheidungen (→ Teil 2). + +R.-P. Mundani, ICS, HS 2024 Motivation aus der Stochastik: Ziegenproblem (a.k.a. Monty-Hall-Dilemma). Spielshow: 3 Türen – zwei Nieten (Ziege) und ein Gewinn (Auto). SpielerIn wählt Tür, ShowmasterIn öffnet andere Tür (Ziege)  Strategie: Wahl ändern…? Die anderen beiden Fälle (SpielerIn wählt Tür B oder C) sind symmetrisch. In drei Fällen (3/9) Verlust, in sechs Fällen (6/9) Gewinn  Wahl ändern  Quellen: depositphotos.com, jamara.com A B C A B C A B C ACB + +R.-P. Mundani, ICS, HS 2024 Einführung in die Spieltheorie: Grundlagen, Definition: Spieltheorie, Teilgebiet der Mathematik („Theorie des strategischen Denkens“), Modellierung von Entscheidungssituationen. Spieler treffen ihre Wahl ein für alle Mal (nur ein Durchlauf) und gleichzeitig. Es gibt eine übliche Darstellung in Normalform (z.B. Gefangenendilemma). Sequentielle Spiele: Es gibt eine spezifische Ordnung, welche(r) SpielerIn wann entscheiden darf. In jedem Schritt haben SpielerInnen, die entscheiden dürfen, vollständiges oder unvollständiges Wissen über den aktuellen Status (d.h. frühere Entscheidungen). Die übliche Darstellungsform ist extensiv oder als Baum. + +R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien: Gefangenendilemma (das wohl bekannteste nicht-kooperative Spiel in reinen Strategien). Hintergrund: Zwei Gefangene werden beschuldigt, gemeinsam ein Verbrechen verübt zu haben. Beide Gefangenen werden getrennt vernommen und können nicht kommunizieren. Mangels konkreter Beweise kann beiden nur ein Teil der Tat nachgewiesen werden. Mögliche Aktionen: Leugnen (L): niedrige Strafe (2 Jahre). Gestehen (G): hohe Strafe (5 Jahre) oder Höchststrafe (8 Jahre Kronzeugenregel). Maximierung der Auszahlung. Funktion $u_i$ modelliert den Gewinn an Freiheit (also Höchststrafe tatsächlicher Strafe). + +R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien: Gefangenendilemma (Fortsetzung). Darstellung in Normalform (bei zwei SpielerInnen auch Bimatrixform genannt). Frage: Wie finden beide SpielerInnen die für sich beste Strategie…? + +$$ +\begin{array}{c|cc} + & G & L \\ +\hline +G & (6, 6) & (8, 0) \\ +L & (0, 8) & (3, 3) \\ +\end{array} +$$ + +$A_2A_1(u_1,u_2)$ + +R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien: Ein bisschen Mathematik . Nash-Gleichgewicht: Ein Aktionsprofil ist ein Nash-Gleichgewicht, wenn kein Spieler durch einseitige Änderung seiner Strategie seinen Nutzen verbessern kann. Wenn eine Strategie $a_i$ für alle Strategien $a_i$ eines Spielers einen höheren Nutzen liefert, dann wird von starker Dominanz gesprochen, d.h. Strategie $a_i$ ist stark dominant über Strategie $a_i$. Profile dominanter Strategien führen zu Nash-Gleichgewichten (Umkehrung nicht gültig). Nochmals Frage: Was bedeutet das für das Gefangenendilemma…? + +R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien: Ein bisschen Mathematik (Fortsetzung). Stark dominante Strategie beider SpielerInnen ist G. + +R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien: Übung: Steuerhinterziehung (Forts.). Kreative Buchführung 200M CHF nicht versteuert. Wird Zanoma durch privaten Steuerprüfer die Steuerhinterziehung nachgewiesen, so muss das Unternehmen eine Nachzahlung von $s$ Millionen CHF (Steuer plus Busse) leisten. Steuerprüfer bekommt im Erfolgsfall 5% der Nachzahlung als Prämie. Steuerprüfer hat Strategien Prüfung (P; Kosten: 10M CHF) und keine Prüfung (KP; Kosten: 0). Zanoma hat Strategien Steuerhinterziehung (S) und keine Steuerhinterziehung (KS). Fragen: a) Wie sieht die dargestellte Situation als Spiel in Bimatrixform aus…? b) Gibt es für $s<200$M CHF dominante Strategien und Nash-Gleichgewichte…? Jede Ähnlichkeit mit real existierenden Unternehmen ist rein zufällig. + +R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien: Konservative Strategien. In ungünstigsten Fällen: MinimiererIn P1: $U^- = \min_i \max_j a_{ij}$. Für jede Strategie i: Maximiere über alle Spalten j ( größtmöglicher Verlust). Wähle Minimum aller Spaltenmaxima  konservative Strategie i* (Obergrenze $U^-$). MaximiererIn P2: $U^+ = \max_j \min_i a_{ij}$. Für jede Strategie j: Minimiere über alle Zeilen i ( kleinstmöglicher Gewinn). Wähle Maximum aller Zeilenminima  konservative Strategie j* (Untergrenze $U^+$). Minimieren des maximal möglichen Verlustes und Maximieren des minimal möglichen Gewinnes. Zwei-Personen-Nullsummenspiele haben immer einen Sattelpunkt. + +R.-P. Mundani, ICS, HS 2024 Spiele in gemischten Strategien: Einführung von Wahrscheinlichkeiten. SpielerIn P1: Spielt mit Wahrscheinlichkeit $p$ Strategie $S_{11}$ und mit $1-p$ Strategie $S_{12}$. SpielerIn P2: Spielt mit Wahrscheinlichkeit $q$ Strategie $S_{21}$ und mit $1-q$ Strategie $S_{22}$. Für den Fall $n>2$ Strategien: Wahrscheinlichkeiten $p_1, p_2, \dots, p_n$ je Strategie mit $\sum_{i=1}^n p_i = 1$. Bestimmung des Sattelpunkts. Multiplizieren des 2PNS-Spiels mit $p$ und $q$. + +R.-P. Mundani, ICS, HS 2024 Spiele in gemischten Strategien: Nochmal Aufgabe: Steuerhinterziehung (Fortsetzung). Aus Sicht von Zanoma: $(200-s) \cdot q + 200 \cdot (1-q) = 0 \cdot q + 0 \cdot (1-q) \iff q = 200/s$. Aus Sicht des Steuerprüfers: $(0.05 \cdot s - 10) \cdot p - 10 \cdot (1-p) = 0 \cdot p + 0 \cdot (1-p) \iff p = 200/s$. D.h. Nash-Gleichgewicht in gemischten Strategien somit für $p=q$, d.h. für $(200/s, 200/s)$. In anderen Worten: Höhere Nachzahlungen (Bussen) senken die Wahrscheinlichkeit, dass der Steuerprüfer eine Prüfung durchführt und die Wahrscheinlichkeit, dass Zanoma eine Steuerhinterziehung begeht. + +R.-P. Mundani, ICS, HS 2024 Gruppenentscheidungen: Satz von Arrow. Mit $|A| > 2$ sei eine Menge von Auswahlmöglichkeiten. $K: P^A \to P^A$ sei kollektive Auswahlfunktion, die Pareto-Bedingung und Unabhängigkeit von irrelevanten Alternativen erfüllt. Dann existiert immer ein Diktator: Es gibt ein $d \in I$: Für alle $(\rho_1, \dots, \rho_n) \in P^A$: Für alle $(x, y) \in A \times A$: $x \rho_d y \implies x \rho y$. ABER: Diktator ist „großzügiger“ gegenüber anderen Gruppenmitgliedern. + + +--- + +## Game Theory Basics + +Nicht-kooperative Spiele zeichnen sich dadurch aus, dass alle Spieler ihre Auszahlung maximieren, indem sie ihre beste Strategie wählen – basierend auf ihrem Wissen oder ihren Erwartungen bezüglich der Strategien der anderen Spieler. Vereinbarungen zwischen den SpielerInnen bestehen nicht. Im Gegensatz dazu stehen kooperative Spiele, bei denen alle SpielerInnen nach gemeinsamen Aktionen suchen, die optimal für die Gruppe sind. Vor dem Spiel können Absprachen zwischen den SpielerInnen getroffen werden. Die Unterscheidung zwischen diesen beiden Spieltypen liegt also in der Möglichkeit und dem Vorhandensein von Absprachen und der Zielsetzung der SpielerInnen: individuelle Nutzenmaximierung versus kollektive Optimierung. + + +--- + +## Game Theory Strategies + +Mixed Strategies Games + +Group Decision Making + +Ranking Preferences + +Preference Relations + +Collective Choice + +Condorcet Voting + +Decision Procedure Conditions + +# Spieltheorie und Gruppenentscheidungen + +Vorhersehbarkeit durch Mitspieler*innen ist ein wichtiger Aspekt. Ein Beispiel: Schere (100%), Stein (0%), Papier (0%). Gemischte Strategien, bei denen eine Spielerin zufällig zwischen Strategien entscheidet, werden hingegen durch eine Wahrscheinlichkeitsfunktion über reinen Strategien charakterisiert. Beispiel: Schere (25%), Stein (50%), Papier (25%). Reine Strategien führen jedoch häufig nicht zu Nash-Gleichgewichten. Nicht jedes Zwei-Personen-Nullsummenspiel (2PNS-Spiel) in reinen Strategien hat einen Sattelpunkt. Betrachten wir das Beispiel: + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & 3 & -1 \\ +S_{12} & -1 & 5 \\ +\end{array} +$$ + +$\min_i \max_j \to i^* = S_{11}$ und $U^- = 3$, $\max_j \min_i \to j^* = S_{22}$ und $U^+ = 1$. Zwei-Personen-Nullsummenspiele in gemischten Strategien haben immer einen Sattelpunkt. Das Min-Max-Theorem wird erneut betrachtet. + +Situationen mit unerwünschten Ergebnissen sind in der Regel nicht vermeidbar, dies gilt sowohl für die Wahl der Strategien und der Entscheidungsverfahren selbst. + +Rangabbildungen werden verwendet, um Präferenzen zu repräsentieren. Die Situation: eine endliche Menge $A$ von Möglichkeiten (z.B. Kandidat*innen, Sänger*innen, Pläne, …). Präferenzen entstehen durch die Vergabe von Rangnummern $r(x)$ mit $x \in A$. Für zwei Möglichkeiten $x, y \in A$ bedeutet $r(x) < r(y)$, dass $x$ gegenüber $y$ bevorzugt wird. Zwei (oder mehr) Möglichkeiten können dieselbe Rangnummer erhalten. Eine Rangabbildung $r: A \to P$ ist eine surjektive Abbildung der Menge der Möglichkeiten $A$ auf eine Menge von Präferenzen $P = \{1, \dots, k\} \subset \mathbb{N}$. Beispiel: + +$A = \{x, y, z\}$, $P = \{1, 2\}$; $r(x) = 1$, $r(y) = r(z) = 2$. Surjektivität: Jedes Element der Bildmenge hat (mindestens) ein zugehöriges Element in der Urbildmenge. + +Eigenschaften von Relationen: transitiv: mit $xRy$ und $yRz$ folgt auch $xRz$ (z.B. aus $2 < 3$ und $3 < 4$ folgt $2 < 4$). Reflexiv: $xRx$ gilt für alle $x \in A$ (z.B. $2 \le 2$). Asymmetrisch: $xRy$ und $yRx$ gelten niemals gleichzeitig (z.B. $2 < 3$, aber nicht $3 < 2$). Transitive und reflexive Relationen werden auch Quasiordnungen genannt. Rangabbildungen eignen sich vor allem für Visualisierungen (z.B. Fußballtabellen). + +$x \rho y \iff \neg(y \rho^* x)$. + +Gruppenentscheidungen erfordern die Berücksichtigung der Präferenzen aller Wähler*innen. Die Menge der Wähler*innen $I = \{1, \dots, n\}$ besteht aus Individuen, die von 1 bis n durchnummeriert sind. Jede/r Wähler*in hat eine persönliche Präferenz $P_A$. Gesucht ist eine kollektive Auswahlfunktion. + +Mehrheitsentscheidung (Condorcet-Verfahren): Im direkten Vergleich zweier Elemente $x, y \in A$ gibt es Individuen $\{i \in I: x \rho_i y\}$, die $x$ bevorzugen, Individuen $\{i \in I: y \rho_i x\}$, die $y$ bevorzugen, und Individuen, die bezüglich $x$ und $y$ indifferent sind. Das Condorcet-Verfahren zählt, wer mehr Vergleiche gewinnt. Für zwei Elemente $x, y$ gilt: $K(ρ_1, \dots, ρ_n)$ ist eine Relation $ρ$. Das Verfahren ist für beliebige $ρ$ durchführbar, aber die Relation $ρ$ ist im Fall von mehr als zwei Möglichkeiten nicht immer transitiv, d.h. $ρ$ ist keine zulässige Präferenzrelation aus $P_A$. + +Beispiel: $I = \{1, 2, 3\}$, $A = \{x, y, z\}$. Es folgt $x \rho y$, $y \rho z$ und $z \rho x$. Aus der Transitivität würde man erwarten $x \rho z$, was aber nicht der Fall ist. Ein einziges Individuum, das $y$ mindestens so schätzt wie $x$ ($y \le x$), führt zu Problemen. In der Praxis liefert das Verfahren keine echten Präferenzen (außerdem: für $|A| > 2$, $ρ \notin P_A$). Beispiel: $i = 1$: $x < y < z$ und $i = 2$: $y < z < x \implies ρ$ enthält genau ein Paar $y \rho z \to ρ \notin P_A$. + +Bedingungen an Auswahlverfahren: Pareto-Bedingung (Einstimmigkeit): Kollektive Auswahlfunktion $K: P_A \to P_A$ erfüllt die Pareto-Bedingung, wenn für alle $ρ_i \in P_A$ gilt: für alle $x, y \in A$, wenn $x \rho_i y$ für alle $i$, dann $x \rho y$. Somit scheidet der externe Diktator aus, alle anderen Verfahren erfüllen die Bedingung. Beispiel: Beide Individuen sehen $x, y$ vor $z$. + +Unabhängigkeit von irrelevanten Alternativen: Die Platzierung einer dritten Möglichkeit $z$ soll keinen Einfluss auf die relative Platzierung von $x$ und $y$ haben. Eine Präferenzänderung bei einem Individuum kann die kollektive Präferenz beeinflussen, auch wenn die relative Ordnung zwischen $x$ und $y$ unverändert bleibt. + + +--- + +## Game Theory Basics + +# Einführung in die Spieltheorie + +Die Spieltheorie modelliert Entscheidungssituationen, oft im sozialen Konflikt. Der Erfolg hängt dabei nicht nur von den eigenen, sondern auch von den Entscheidungen anderer ab. Anwendungsgebiete finden sich vor allem in den Wirtschaftswissenschaften und der Ökonomie. Bekannte Vertreter sind John von Neumann und John Forbes Nash. Man unterscheidet zwischen kooperativen und nicht-kooperativen Spielen sowie simultanen und sequentiellen Spielen. + +Entscheidungen können unter Gewissheit getroffen werden (alle Aktionen und Konsequenzen sind bekannt), unter Risiko (bestimmte Wahrscheinlichkeiten von Aktionen und Konsequenzen sind bekannt) oder unter Unsicherheit (keine Aktionen und Konsequenzen sind bekannt). Die Modellierung umfasst eine Menge von SpielerInnen $N = \{1, 2, \dots, n\}$, eine Menge der Aktionen (auch Strategien genannt) $A_i$ von SpielerIn $i$ für alle $i \in N$, und eine Menge der Aktionsprofile (auch Strategieprofile) $A = \{(a_i)_{i \in N}, a_i \in A_i \text{ für alle } i \in N\}$. Die Auszahlungsfunktion $u_i: A \to \mathbb{R}$ beschreibt die Auszahlung für SpielerIn $i$. Ein Beispiel: Die Entscheidung zwischen zwei Möglichkeiten mit Aktionen $A_1 = \{X, Y\}$ und ... + +Ein Nash-Gleichgewicht liegt vor, wenn keine SpielerIn durch einseitige Abweichung von ihrer Entscheidung eine höhere Auszahlung erreichen kann, während alle anderen SpielerInnen bei ihren Entscheidungen bleiben. Formal: Ein Aktionsprofil bildet genau dann ein Nash-Gleichgewicht, wenn keine der SpielerInnen durch Abweichung davon eine höhere Auszahlung erhält. + +Zwei-Personen-Nullsummenspiele zeichnen sich dadurch aus, dass der Gewinn einer SpielerIn dem Verlust der anderen entspricht (die Summe ist immer null). Sie werden oft in Matrixform dargestellt. Konservative Strategien führen zu MinimiererInnen oder MaximiererInnen. Spiele in gemischten Strategien ermöglichen die Betrachtung von Wahrscheinlichkeitsverteilungen über die reinen Strategien. Gleichgewichtspunkte oder Sattelpunkte sind dabei von Interesse. Ein Beispiel ist das Spiel "Chicken" (Hühner-Spiel), bei dem zwei SpielerInnen (z.B. mit Autos) auf einer Kollisionskurs aufeinander zufahren und durch (vorausschauendes) Verhalten entscheiden müssen. Die USA nutzten dieses Spiel für strategische Analysen während der Kuba-Krise. Das Spiel wird interessant, wenn die Kosten für den Kampf ($C$) die Kosten bzw. den Wert des Sieges ($V$) überschreiten, d.h. $C > V > 0$. + +Eine Übung zur Steuerhinterziehung veranschaulicht dominante Strategien. Unternehmen Zanoma hat durch kreative Buchführung 200 Millionen CHF nicht versteuert. Falls $s < 200$, folgt $200 - s > 0$, somit ist S eine strikt dominante Strategie. Falls $s < 200$, folgt $0.05 * s - 10 < 0$, somit ist KP eine strikt dominante Strategie. + +Spiele in gemischten Strategien können alternativ als reine Strategien aus der jeweiligen Sicht der SpielerInnen P1 und P2 berechnet werden. Ein Sattelpunkt kann beispielsweise bei (0.5, 0.25) liegen. Durch Einsetzen von $p$ in $S_{21}$ oder $S_{22}$ sowie $q$ in $S_{11}$ oder $S_{12}$ ergibt sich ... Eine alternative Darstellung über `f(x,y) = -8*x*y + 2*x + 4*y + 1` und `splot [0:1][0:1] f(x,y)` ist möglich. + +Gruppenentscheidungen befassen sich mit der gemeinsamen Bestimmung einer Rangfolge von Möglichkeiten (z.B. Wahlen, Wettbewerbe). Nicht alle werden mit der Rangfolge einverstanden sein. Ein axiomatischer Ansatz stellt Eigenschaften auf und prüft, welche Entscheidungsverfahren diese erfüllen. Die Modellierung individueller Präferenzen und der Entscheidungsverfahren selbst ist essentiell. Bedingungen an Auswahlverfahren beinhalten die Unabhängigkeit von irrelevanten Alternativen. Ein Beispiel: $I = \{1, 2\}$, $A = \{x, y, z\}$ mit Rangaddition KA als Auswahlverfahren. Als Gruppenentscheidung bzgl. x und y gilt $y \rho' x$, aber nicht $x \rho y$. Das Ergebnis ändert sich, obwohl kein Individuum die Präferenz im direkten Vergleich zwischen x und y ändert (i=1 sieht x < y und i=2 ...). + diff --git a/Code/pipeline-example/output/20250406_172825/clustered_rewritten_gemini.md b/Code/pipeline-example/output/20250406_172825/clustered_rewritten_gemini.md new file mode 100644 index 0000000..b39c9a8 --- /dev/null +++ b/Code/pipeline-example/output/20250406_172825/clustered_rewritten_gemini.md @@ -0,0 +1,235 @@ +# Clustered and Rewritten Text Document + +This document groups text chunks based on semantic similarity. Topics and rewritten text (formatted in Markdown with LaTeX for equations) are generated by the Gemini API. + +## Game Theory Basics +Nash Equilibrium +Dominant Strategies +Prisoner's Dilemma +Zero-Sum Games +Conservative Strategies +Mixed Strategies +Checkpoints Summary +Group Decisions +Collective Choice + +# Spieltheorie: Eine Zusammenfassung + +Nicht-kooperative Spiele sind in der Literatur und in Anwendungen weit verbreitet. Spieler können keine Absprachen vor dem Spiel treffen und haben keinerlei Vereinbarungen über gemeinsame Aktionen, die optimal für die Gruppe sind. Eine zweite Unterscheidung betrifft simultane Spiele, bei denen alle Spieler ihre Wahl ein für alle Mal treffen (nur ein Zug). + +Spielen in reinen Strategien: Eine dominante Strategie $a_i$ ist mindestens so gut wie eine andere Strategie $a_i'$ für alle möglichen Aktionen der anderen Spieler $a_j \in A_j \neq i$, d.h. für alle $a_j \in A_j \neq i$ gilt $u_i(a_i, a_{-i}) \ge u_i(a_i', a_{-i})$. Gilt die Ungleichung im strengen Sinn ($>$), spricht man von starker Dominanz. Ein Nash-Gleichgewicht liegt vor, wenn kein Spieler durch eine einseitige Abweichung davon eine höhere Auszahlung erzielen kann. Das bedeutet, für alle $a_i \in A_i$ und $i \in N$ gilt: $u_i(a_i^*, a_{-i}^*) \ge u_i(a_i, a_{-i}^*)$. Was bedeutet das für das Gefangenendilemma…? + +Weiteres Beispiel: Falke-und-Taube-Spiel. Zwei Wettkämpfer müssen sich zwischen aggressivem (Falke) und nicht-aggressivem (Taube) Verhalten entscheiden. Die dominante Strategie beider Spieler ist G. ABER: Gleichgewicht (G, G) ist ineffizient, da (L, L) eine bessere Auszahlung ergeben würde. + +$$ +\begin{array}{c|cc} + & G & L \\ +\hline +G & (6, 6) & (8, 0) \\ +L & (0, 8) & (3, 3) \\ +\end{array} +$$ + +Zwei-Personen-Nullsummenspiele: Spieler versuchen, den Gewinn zu maximieren (d.h. den Gewinn des anderen zu minimieren); der Verlust des einen ist der Gewinn des anderen (d.h. die Summe ist immer Null). Die Darstellung von 2PNS-Spielen erfolgt üblicherweise in Normalform (oder Matrixform). + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & (-1, 1) & (-5, 5) \\ +S_{12} & (1, -1) & (3, -3) \\ +\end{array} +$$ + +Konservative Strategien: Die beste gegnerische Strategie führt zum eigenen schlechtesten Gewinn. Eine konservative Strategie wählt den besten Wert aus allen ungünstigsten Fällen. Der Minimierer P1 sucht $\max_i \min_j u_{1ij}$, der Maximierer P2 sucht $\min_j \max_i u_{1ij}$ (Schnittpunkt von Minimum und Maximum). Die Idee bleibt gleich: Keine Spielerin kann sich durch einen einseitigen Strategiewechsel verbessern. + +Beispiel: Schlacht in der Bismarcksee. Falls die Amerikaner die richtige Route überprüfen, kann die Bombardierung sofort begonnen werden (andernfalls verbleiben zwei Tage für die andere Route). Auf der Nordroute muss wegen schlechter Sicht die Bombardierung zudem einen Tag ausbleiben. Damit ergibt sich folgendes 2PNS-Spiel (Gewinn ≡ mögliche Tage für Bombardierung): + +$$ +\begin{array}{c|cc} + & N & S \\ +\hline +N & (2, -2) & (1, -1) \\ +S & (0, 0) & (2, -2) \\ +\end{array} +$$ + +$\implies i^* = N$ und $U^- = 2 \implies j^* = N$ und $U^+ = 2 \implies$ Sattelpunkt bei (N, N). + +Spielen in gemischten Strategien: Reine Strategien legen *a priori* eine Strategie fest. Nachteil: Vorhersehbarkeit durch Mitspieler. Im Fall eines Sattelpunkts wird das 2PNS-Spiel mit Wahrscheinlichkeiten $p$ und $q$ multipliziert. Die Lösung der zugehörigen Gleichung erfolgt oft durch lineare Programmierung. + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & 1 & -5 \\ +S_{12} & 3 & -1 \\ +\end{array} +$$ + +$\implies$ Sattelpunkt der Funktion (graphisch) bei $p = 0.5$ und $q = 0.25$. + +Checkpoints: Spiele in reinen Strategien, Darstellung in Normalform/Bimatrixform, dominante Strategie, Nash-Gleichgewicht, Zwei-Personen-Nullsummenspiele. + +Gruppenentscheidungen: Gesucht ist eine kollektive Auswahlfunktion $K$ für die Präferenz der Gesamtheit: $K: PA = PA \times PA \times \dots \times PA \to PA$. Wesentliche Bedingungen: Die Auswahlfunktion $K$ muss total sein. Für jede mögliche Kombination von (persönlichen) Präferenzen aus $PA$ muss es eine Gesamtpräferenz (also ein Ergebnis) geben. Das Ergebnis selbst muss wieder eine Relation in $PA$ sein. + + +Entscheidungsverfahren (Fortsetzung): Rangaddition. Jede Individualpräferenz $\rho_i$ legt eine Rangabbildung fest. Die kollektive Präferenz wird aus den Rangnummern der jeweiligen Individuen bestimmt. Einfache Methode: Addition der Rangnummern. Für zwei Elemente x, y gilt: $K_A(\rho_1, \dots, \rho_n)$ ist Relation $\rho$ mit $\sum r_i$. Die Summe $\sum r_i$ ist keine Rangabbildung, kann aber leicht korrigiert werden, sodass $\rho \in PA$ gilt. Das Verfahren hat keine offensichtlichen Nachteile. + + +Kollektive Auswahlfunktionen $PA \to PA$ werden auch von unerwünschten Verfahren erfüllt. Zwei Bedingungen, die jedes „gerechte“ Verfahren erfüllen sollte: Pareto-Bedingung: Die Gesamtheit kann für beliebige Wahlmöglichkeiten durch Einstimmigkeit jede gewünschte Rangfolge erzwingen. Unabhängigkeit von irrelevanten Alternativen: Die Rangfolge zwischen zwei Wahlmöglichkeiten wird nur von den Präferenzen bezüglich dieser beiden Möglichkeiten bestimmt. + + +--- + +## Game Theory Basics +Two-Person Zero-Sum Games +Bismarck Sea Battle +Mixed Strategies Game +Group Decisions +Preferences & Relations +Decision Procedures +Arrow's Impossibility Theorem + +# Spieltheorie: Eine Einführung + +Zwei-Personen-Nullsummenspiele (2PNS-Spiele) werden auch als MinMax-Spiele bezeichnet. In Matrixform wird Spieler 1 zur Minimiererin (Zeilenspielerin) und Spieler 2 zur Maximiererin (Spaltenspielerin). Aufgrund der speziellen Struktur von 2PNS-Spielen nehmen Nash-Gleichgewichte die Form von Sattelpunkten an (Schnittpunkt von Minimum und Maximum). Der Min-Max-Theorem besagt: Ein Sattelpunkt existiert genau dann, wenn $U^- = a_{i^*j^*} = U^+$. Ein Beispiel hierfür ist die Schlacht in der Bismarcksee (2.-4. März 1943): Japanische Truppen sollen von Rabaul nach Lae verlegt werden, wobei zwei Routen zur Verfügung stehen: eine regnerische Nordroute und eine sonnige Südroute. Der japanische Konvoi ist unabhängig von der gewählten Route drei Tage unterwegs. Die U.S. Air Force möchte den Konvoi bombardieren, hat aber nicht genügend Flugzeuge, um beide Routen gleichzeitig zu überwachen. Die Amerikaner müssen eine Entscheidung treffen. + +Möglichkeiten: Aktionen: $A_1 = \{X, Y\}$ und $A_2 = \{X, Y\}$. Aktionsprofile: $A = \{(X, X), (X, Y), (Y, X), (Y, Y)\}$. Auszahlungsfunktion: $u_{1,2}: A \to (1, 0, 0, 1)$. Wie verändert sich das Spiel, wenn folgende Auszahlungsfunktionen gelten? Spieler 1: $u_1: A \to (1, 0, 0, 0)$; Spieler 2: $u_2: A \to (0, 0, 0, 1)$. + +Spielen in gemischten Strategien: Aufgabe: Steuerhinterziehung. Wie ändert sich die Situation für den Fall $s > 200$ Mio. CHF? Problem: Keine dominante Strategie / kein Nash-Gleichgewicht in reinen Strategien. Wenn $q$ in $S_{22}$ sowie $p$ in $S_{11}$ oder $S_{12}$ gilt, ergibt sich $U^- = 2 = U^+$. + +Gruppenentscheidungen: Relationen beschreiben Beziehungen (z.B. Rangfolgen, Präferenzen) zwischen Elementen. Darstellung als Relation $R$ auf Paaren $(x, y)$ von Elementen aus $A$, formal geschrieben als $(x, y) \in R$ oder abkürzend als $xRy$. Beispiel: Mit $R$ ist ‘$<$’ (kleiner) und $xRy$ folgt $x < y$. Eigenschaften von Relationen... + +Präferenzen: Rangabbildungen definieren eine Präferenzrelation: $x \rho y \iff r(x) < r(y)$. Relation $\rho$ ist transitiv und asymmetrisch. Die Menge aller (möglichen) darstellbaren Relationen auf $A$ ist definiert als $P_A := \{\rho \subset A \times A: \rho \text{ erfüllt } x \rho y \iff r(x) < r(y) \text{ für eine Rangabbildung } r\}$. Erweiterung der Relation durch alle Paare mit gleichem Rang: $x \rho^* y \iff r(x) \le r(y)$. Relation $\rho^*$ ist transitiv und reflexiv. Es gilt offensichtlich $\rho \subset \rho^*$. Analog zur Menge $P_A$ lässt sich damit definieren $P_A^* := \{\rho^* \subset A \times A: \rho^* \text{ erfüllt } x \rho^* y \iff r(x) \le r(y) \text{ für eine Rangabbildung } r\}$. Beispiel: $A = \{x, y, z\}$, $r(x) = 1$, $r(y) = r(z) = 2$. Zwischen beiden Relationen gilt folgende Beziehung: $x \rho y \iff \neg(y \rho^* x)$. Rangabbildungen vor allem für... + +Entscheidungsverfahren: Extrembeispiel 1: Externer Diktator. Für beliebiges $\rho_E \in P_A$ gilt... Ergebnis ist offensichtlich Abbildung $P_A \to P_A$, aber unabhängig von den $\rho_i$. Verfahren kann kaum als „gerecht“ oder „demokratisch“ bezeichnet werden. Extrembeispiel 2: Interner Diktator. Festlegung eines Individuums $d \in I$, dessen Präferenz $\rho_d$ das Ergebnis bestimmt. Damit gilt... Rangaddition: Beispiel: $I = \{1, 2\}$, $A = \{x, y, z\}$. Rangaddition hat keine offensichtlichen Nachteile, liefert aber ggf. unschöne Ergebnisse. + +Mehrheit: Es folgt $x \rho y$, $y \rho z$ und $z \rho x$. Aus der Transitivität folgt schließlich $x \rho x \to$ kein gültiges Ergebnis, d.h. $\rho \notin P_A$. Einstimmigkeit: Element $x$ wird Element $y$ vorgezogen $\iff$ alle Individuen teilen diese Präferenz für alle. ABER: Ein einziges Individuum, das $y$... Rangfolge zwischen zwei Wahlmöglichkeiten kann nicht durch Präferenzänderungen der Individuen im Hinblick auf eine dritte Wahlmöglichkeit gekippt werden. Problem: Bereits mit diesen zwei Forderungen kein annähernd demokratisches Verfahren mehr möglich (Satz von Arrow). Quintessenz: Demokratie funktioniert nicht…? + +Bedingungen an Auswahlverfahren: Möglichkeit $z$ soll keinen Einfluss auf Ergebnis haben. Oder formal: Wenn kein Individuum Präferenz $\rho_i, \rho_i' \in P_A$ bzgl. $x$ und $y$ ändert, soll gelten (für alle) $\implies$ (...). Rangaddition somit als „gerechtes“ Verfahren ausgeschlossen. Condorcet-Verfahren und Einstimmigkeit liefern für $|A| > 2$ evtl. ungültiges Ergebnis. Nur interner Diktator $K_D$ erfüllt alle Bedingungen. Der Diktator ist „großzügiger“ gegenüber anderen Gruppenmitgliedern. Bei Indifferenz (es gilt weder $x \rho_d y$ noch $y \rho_d x$) beliebige Rangfolgen von $x, y$ zulässig. US-Wahlen haben nur zwei Kandidaten. + + +--- + +## Game Theory Introduction + +# Einführung in die Spieltheorie (CDS-1012) HS 2024 Prof. Dr. rer. nat. habil. Ralf-Peter Mundani, DAViS + +**Einleitung:** Ein Überblick über die Spieltheorie, ein Teilgebiet der Mathematik, das das strategische Denken modelliert. Wir behandeln die Modellierung von Entscheidungssituationen, Spiele in reinen Strategien, das Nash-Gleichgewicht und dominante Strategien, Zwei-Personen-Nullsummenspiele, konservative Strategien, Spiele in gemischten Strategien und die Berechnung von Sattelpunkten. Mehrheitsbeschlüsse und Gruppenentscheidungen werden im zweiten Teil behandelt. + +**Motivation aus der Stochastik: Das Ziegenproblem (a.k.a. Monty-Hall-Dilemma):** Eine Spielshow mit drei Türen – zwei Ziegen und ein Auto. Der Spieler wählt eine Tür, der Showmaster öffnet eine andere Tür mit einer Ziege. Sollte der Spieler seine Wahl ändern? Die Fälle, in denen der Spieler Tür B oder C wählt, sind symmetrisch. In drei von neun Fällen verliert der Spieler, in sechs von neun Fällen gewinnt er. Das Ändern der Wahl ist also vorteilhaft. + +**Grundlagen:** Die Spieltheorie modelliert Entscheidungssituationen, in denen die Akteure (Spieler) ihre Wahl gleichzeitig und nur einmal treffen (ein Durchlauf). Eine übliche Darstellung ist die Normalform (z.B. Gefangenendilemma). Sequentielle Spiele weisen eine spezifische Ordnung auf, die bestimmt, welcher Spieler wann entscheidet. Spieler haben in jedem Schritt vollständiges oder unvollständiges Wissen über den aktuellen Status (frühere Entscheidungen). Die übliche Darstellungsform ist extensiv oder als Baum. + +**Spiele in reinen Strategien:** Das Gefangenendilemma ist ein bekanntes nicht-kooperatives Spiel in reinen Strategien. Zwei Gefangene werden beschuldigt, gemeinsam ein Verbrechen begangen zu haben. Sie werden getrennt vernommen und können nicht kommunizieren. Mangels Beweisen kann beiden nur ein Teil der Tat nachgewiesen werden. Mögliche Aktionen sind Leugnen (L) mit einer niedrigen Strafe (2 Jahre) und Gestehen (G) mit einer hohen Strafe (5 Jahre) oder Höchststrafe (8 Jahre – Kronzeugenregel). Die Auszahlung wird durch die Funktion $u_i$ modelliert, die den Gewinn an Freiheit (Höchststrafe – tatsächliche Strafe) darstellt. + +**Gefangenendilemma (Fortsetzung):** Die Darstellung in Normalform (bei zwei Spielern auch Bimatrixform genannt) lautet: + +| | G | L | +| :---- | :---- | :---- | +| **G** | (6, 6) | (8, 0) | +| **L** | (0, 8) | (3, 3) | + +Die Frage ist: Wie finden beide Spieler die für sie beste Strategie? + +**Ein bisschen Mathematik:** Das Nash-Gleichgewicht ist ein Aktionsprofil, bei dem kein Spieler einen Anreiz hat, seine Strategie einseitig zu ändern. Eine stark dominante Strategie $a_i$ ist besser als jede andere Strategie $a_i$ unabhängig von den Strategien der anderen Spieler. Profile dominanter Strategien führen zu Nash-Gleichgewichten (die Umkehrung gilt nicht). Was bedeutet das für das Gefangenendilemma? + +**Stark dominante Strategie:** Die stark dominante Strategie beider Spieler ist G. + +**Übung: Steuerhinterziehung:** Ein Unternehmen, Zanoma, hat 200 Millionen CHF nicht versteuert. Wird die Steuerhinterziehung nachgewiesen, muss das Unternehmen eine Nachzahlung von $s$ Millionen CHF (Steuer plus Busse) leisten. Der Steuerprüfer erhält im Erfolgsfall 5% der Nachzahlung als Prämie. Der Steuerprüfer hat die Strategien Prüfung (P; Kosten: 10M CHF) und keine Prüfung (KP; Kosten: 0). Zanoma hat die Strategien Steuerhinterziehung (S) und keine Steuerhinterziehung (KS). + +a) Wie sieht die Situation als Spiel in Bimatrixform aus? + +b) Gibt es für $s < 200$M CHF dominante Strategien und Nash-Gleichgewichte? + +**(jede Ähnlichkeit mit real existierenden Unternehmen ist rein zufällig)** + +**Konservative Strategien:** Konservative Strategien minimieren den maximal möglichen Verlust (Minimax). Der Minimierer $P_1$: $U^- = \min_i \max_j a_{ij}$. Der Maximierer $P_2$: $U^+ = \max_j \min_i a_{ij}$. + +**Spiele in gemischten Strategien:** Spiele in gemischten Strategien haben immer einen Sattelpunkt. Spieler $P_1$ spielt mit Wahrscheinlichkeit $p$ Strategie $S_{11}$ und mit $1-p$ Strategie $S_{12}$. Spieler $P_2$ spielt mit Wahrscheinlichkeit $q$ Strategie $S_{21}$ und mit $1-q$ Strategie $S_{22}$. Für den Fall $n > 2$ Strategien werden Wahrscheinlichkeiten $p_1, p_2, \dots, p_n$ verwendet. + +**Steuerhinterziehung (Fortsetzung):** Aus Sicht von Zanoma: $(200 - s) \cdot q + 200 \cdot (1 - q) = 0 \cdot q + 0 \cdot (1 - q) \iff q = \frac{200}{s}$. Aus Sicht des Steuerprüfers: $(0.05 \cdot s - 10) \cdot p - 10 \cdot (1 - p) = 0 \cdot p + 0 \cdot (1 - p) \iff p = \frac{200}{s}$. Das Nash-Gleichgewicht in gemischten Strategien ist somit für $p = q$, d.h. für $(\frac{200}{s}, \frac{200}{s})$. Höhere Nachzahlungen (Bussen) senken die Wahrscheinlichkeit der Prüfung. + + +**Gruppenentscheidungen:** Der Satz von Arrow: Sei $A$ mit $|A| > 2$ eine Menge von Auswahlmöglichkeiten. $K: PA \to PA$ sei eine kollektive Auswahlfunktion, die die Pareto-Bedingung und Unabhängigkeit von irrelevanten Alternativen erfüllt. Dann existiert immer ein Diktator $d \in I$: Für alle $(\rho_1, \dots, \rho_n) \in PA$: Für alle $(x, y) \in A \times A$: $x \rho_d y \implies x \rho y$. ABER: Der Diktator ist „großzügiger“ gegenüber anderen. + + +--- + +## Game Theory Basics + +Nicht-kooperative Spiele zeichnen sich dadurch aus, dass alle Spieler ihre Auszahlung maximieren, indem sie ihre beste Strategie wählen – basierend auf ihrem Wissen oder ihren Erwartungen bezüglich der Strategien der anderen Spieler. Vereinbarungen zwischen den SpielerInnen gibt es nicht. Im Gegensatz dazu stehen kooperative Spiele, bei denen alle SpielerInnen nach gemeinsamen Aktionen suchen, die optimal für die gesamte Gruppe sind. In kooperativen Spielen können die SpielerInnen vor Spielbeginn Absprachen treffen. + + +--- + +## Game Theory Strategies + +Mixed Strategies + +Group Decision Making + +Preference Rankings + +Decision Procedures + +# Gruppenentscheidungen und Entscheidungsverfahren + +Vorhersehbarkeit durch Mitspieler*innen ist ein wichtiges Thema. Ein Beispiel: Schere (100%), Stein (0%), Papier (0%) repräsentiert eine reine Strategie. Gemischte Strategien hingegen bedeuten, dass eine Spieler*in zufällig zwischen Strategien entscheidet. Formal lässt sich dies als Wahrscheinlichkeitsfunktion über (reinen) Strategien charakterisieren. Beispiel: Schere (25%), Stein (50%), Papier (25%). Reine Strategien führen aber häufig nicht zu Nash-Gleichgewichten. Nicht jedes Zwei-Personen-Nullsummenspiel (2PNS-Spiel) in reinen Strategien hat einen Sattelpunkt. Betrachten wir das Beispiel: + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & 3 & -1 \\ +S_{12} & -1 & 5 \\ +\end{array} +$$ + +Hier gilt: $\min_i \max_j = 3$ und $\max_j \min_i = 1$. Daher ist $i^* = S_{11}$ und $j^* = S_{22}$, mit $U^- = 3$ und $U^+ = 1$. Zwei-Personen-Nullsummenspiele in gemischten Strategien haben immer einen Sattelpunkt. Dies wird durch das Min-Max-Theorem nochmals verdeutlicht. + +Situationen mit unerwünschten Ergebnissen sind in der Regel nicht vermeidbar, auch abhängig von den gewählten Entscheidungsverfahren. + +Rangabbildungen sind ein hilfreiches Werkzeug bei Gruppenentscheidungen. Gegeben ist eine endliche Menge $A$ von Möglichkeiten (z.B. Kandidat*innen, Pläne...). Präferenzen entstehen durch die Vergabe von Rangnummern $r(x)$ mit $x \in A$. Für zwei Möglichkeiten $x, y \in A$ bedeutet $r(x) < r(y)$, dass $x$ gegenüber $y$ bevorzugt wird. Zwei (oder mehr) Möglichkeiten können dieselbe Rangnummer erhalten. Eine Rangabbildung $r: A \to P$ ist eine surjektive Abbildung der Menge der Möglichkeiten $A$ auf eine Menge von Präferenzen $P = \{1, \dots, k\} \subset \mathbb{N}$. Beispiel: + +$A = \{x, y, z\}$, $P = \{1, 2\}$ +$r(x) = 1$, $r(y) = r(z) = 2$. + +Surjektivität bedeutet, dass jedes Element der Bildmenge mindestens ein zugehöriges Element in der Urbildmenge hat. + +Eigenschaften von Relationen sind wichtig: Transitiv: Mit $xRy$ und $yRz$ folgt auch $xRz$ (z.B. aus $2 < 3$ und $3 < 4$ folgt $2 < 4$). Reflexiv: $xRx$ gilt für alle $x \in A$ (z.B. $2 \le 2$). Asymmetrisch: $xRy$ und $yRx$ gelten niemals gleichzeitig (z.B. $2 < 3$, aber nicht $3 < 2$). Transitive und reflexive Relationen werden auch Quasiordnungen genannt. Rangabbildungen eignen sich vor allem für Visualisierungen (z.B. Fussballtabellen). + +Betrachten wir nun Entscheidungsverfahren. Die Menge der Wähler*innen $I = \{1, \dots, n\}$ besteht aus Individuen, durchnummeriert von 1 bis n. Jede*r Wähler*in hat eine persönliche Präferenz $P_i$. Gesucht ist eine kollektive Auswahlfunktion. + +Mehrheitsentscheidung (Condorcet-Verfahren): Im direkten Vergleich zweier Elemente $x, y \in A$ gibt es Individuen $\{i \in I: x \rho_i y\}$, die $x$ bevorzugen, Individuen $\{i \in I: y \rho_i x\}$, die $y$ bevorzugen, und Individuen, die bezüglich $x$ und $y$ indifferent sind. Das Condorcet-Verfahren zählt, wer mehr Vergleiche gewinnt. Für zwei Elemente $x, y$ gilt: $K_C(\rho_1, \dots, \rho_n)$ ist eine Relation $\rho$. Das Verfahren ist für beliebige $\rho$ durchführbar, aber die Relation $\rho$ ist im Fall von mehr als zwei Möglichkeiten nicht immer transitiv, d.h. $\rho$ ist keine zulässige Präferenzrelation aus $P_A$. Beispiel: $I = \{1, 2, 3\}$, $A = \{x, y, z\}$. Es folgt $x \rho y$, $y \rho z$ und $z \rho x$. Aus der Transitivität würde $x \rho z$ folgen, was aber nicht der Fall ist. In der Praxis liefert das Verfahren keine echten Präferenzen (außerdem: für $|A| > 2$, $\rho \notin P_A$). Beispiel: $i = 1$: $x < y < z$ und $i = 2$: $y < z < x$. $\rho$ enthält genau ein Paar $y \rho z \implies \rho \notin P_A$. + +Bedingungen an Auswahlverfahren: Pareto-Bedingung (Einstimmigkeit): Eine kollektive Auswahlfunktion $K: P_A \to P_A$ erfüllt die Pareto-Bedingung, wenn für alle $\rho_i \in P_A$ gilt: für alle $x, y \in A$ mit $x \rho_i y$ für alle $i \in I$ folgt $x \rho y$. Somit scheidet der externe Diktator aus, alle anderen Verfahren erfüllen die Bedingung. Beispiel: Beide Individuen sehen $x, y$ vor $z$. + +Unabhängigkeit von irrelevanten Alternativen: Die Platzierung einer dritten Möglichkeit $z$ soll keinen Einfluss auf die Rangordnung zwischen $x$ und $y$ haben. Eine Präferenzänderung bei einem Individuum kann die kollektive Präferenz beeinflussen. + + +--- + +## Game Theory Basics + +# Spieltheorie: Grundlagen und Anwendungen + +Die Spieltheorie modelliert Entscheidungssituationen, oft in sozialen Konflikten. Der Erfolg hängt dabei nicht nur von den eigenen, sondern auch von den Entscheidungen anderer ab. Wichtige Anwendungsgebiete sind die Wirtschaftswissenschaften und Ökonomie. Bekannte Vertreter sind John von Neumann und John Forbes Nash. Man unterscheidet zwischen kooperativen und nicht-kooperativen Spielen sowie simultanen und sequentiellen Spielen. + +Grundlagen: Entscheidungen können unter Gewissheit (alle Aktionen und Konsequenzen sind bekannt), Risiko (bestimmte Wahrscheinlichkeiten von Aktionen und Konsequenzen sind bekannt) und Unsicherheit (keine Aktionen und Konsequenzen sind bekannt) getroffen werden. Die Modellierung umfasst: eine Menge von SpielerInnen $N = \{1, 2, \dots, n\}$, eine Menge der Aktionen (auch Strategien genannt) $A_i$ von SpielerIn $i$ für alle $i \in N$, und eine Menge der Aktionsprofile (auch Strategieprofile) $A = \{(a_i)_{i \in N}, a_i \in A_i \text{ für alle } i \in N\}$. Die Auszahlungsfunktion $u_i: A \to \mathbb{R}$ beschreibt die Auszahlung für SpielerIn $i$. Beispiel: Entscheidung zwischen zwei Möglichkeiten mit Aktionen $A_1 = \{X, Y\}$ und ... + +... ein Nash-Gleichgewicht liegt vor, wenn keine SpielerIn durch einseitige Abweichung von ihrer Entscheidung (während alle anderen SpielerInnen bei ihren Entscheidungen bleiben) eine höhere Auszahlung erzielen kann. Formal: Ein Aktionsprofil bildet genau dann ein Nash-Gleichgewicht, wenn keine der SpielerInnen durch Abweichung davon eine höhere Auszahlung erhält. + +Zwei-Personen-Nullsummenspiele: Hier ist der Gewinn einer SpielerIn der Verlust der anderen (die Summe ist immer null). Die USA nutzten diese Spielart während der Kuba-Krise für strategische Analysen. Das Spiel wird interessant, wenn die Kosten für den Kampf ($C$) die Kosten bzw. den Wert des Sieges ($V$) überschreiten, d.h. $C > V > 0$. + +Übung: Steuerhinterziehung. Unternehmen Zanoma hat durch kreative Buchführung 200 Millionen CHF nicht versteuert. Falls $s < 200$, folgt $200 - s > 0$, also ist S eine strikt dominante Strategie. Falls $s < 200$, folgt $0.05 \cdot s - 10 < 0$, also ist KP eine strikt dominante Strategie. + +Spielen in gemischten Strategien: Alternative Berechnung (als reine Strategien) aus der jeweiligen Sicht von SpielerIn P1 und P2. Sattelpunkt bei (0.5, 0.25); durch Einsetzen von $p$ in $S_{21}$ oder $S_{22}$ sowie $q$ in $S_{11}$ oder $S_{12}$ ergibt sich ... $f(x,y) = -8xy + 2x + 4y + 1$ und `splot [0:1][0:1] f(x,y)`. + +Zwei-Personen-Nullsummenspiele: Darstellung in Matrixform. Konservative Strategien (MinimiererIn/MaximiererIn). Spiele in gemischten Strategien. Gleichgewichtspunkte/Sattelpunkt. + +Gruppenentscheidungen: Verschiedene Möglichkeiten (Entscheidungsvarianten: Wahlen, Wettbewerbe, ...). Ziel: Gemeinsame Bestimmung einer Rangfolge der Möglichkeiten. Beispiel: WählerInnen ➡️ KandidatInnen (Wahl), Publikum ➡️ TeilnehmerInnen (Wettbewerb). Nicht alle werden mit der Rangfolge einverstanden sein (Unzufriedenheit). Axiomatischer Ansatz: Aufstellung von Eigenschaften und Prüfung, welche Entscheidungsverfahren sie erfüllen. Modellierung individueller Präferenzen und der Entscheidungsverfahren selbst. + +Bedingungen an Auswahlverfahren: Unabhängigkeit von irrelevanten Alternativen. Beispiel: $I = \{1, 2\}$, $A = \{x, y, z\}$ mit Rangaddition $K_A$ als Auswahlverfahren. Als Gruppenentscheidung bzgl. $x$ und $y$ gilt $y \rho' x$, aber nicht $x \rho y$. Das Ergebnis ändert sich, obwohl kein Individuum die Präferenz im direkten Vergleich zwischen $x$ und $y$ ändert ($i=1$ sieht $x < y$ und $i=2$ ...). + diff --git a/Code/pipeline-example/output/20250410_093118/clustered_rewritten_gemini.md b/Code/pipeline-example/output/20250410_093118/clustered_rewritten_gemini.md new file mode 100644 index 0000000..d8d8646 --- /dev/null +++ b/Code/pipeline-example/output/20250410_093118/clustered_rewritten_gemini.md @@ -0,0 +1,293 @@ +# Clustered and Rewritten Text Document + +This document groups text chunks based on semantic similarity. Topics and rewritten text (formatted in Markdown with LaTeX for equations) are generated by the Gemini API. + +## Game Theory Basics +Nash Equilibrium & Dominance +Zero-Sum Games +Mixed Strategies +Collective Decision Making + +# Spieltheorie: Zusammenfassung + +Nicht-kooperative Spiele sind in der Literatur und in Anwendungen weit verbreitet. Spieler können keine Absprachen vor dem Spiel treffen und haben keinerlei Vereinbarungen über gemeinsame Aktionen, die optimal für die Gruppe sind. Eine Unterscheidung ist die zwischen simultanen Spielen, bei denen alle Spieler ihre Wahl ein für alle Mal treffen (nur ein Zug), und sequentiellen Spielen. + +Spielen in reinen Strategien: Eine dominante Strategie ist eine Strategie $a_i$, die mindestens so gut ist wie eine andere Strategie $a_i' \in A_i$ für alle möglichen Aktionen der anderen Spieler $a_j \in A_j \neq i$ ($a_i \ge a_i'$). Gilt die Ungleichung im strengen Sinn ($a_i > a_i'$), spricht man von starker Dominanz. Ein Nash-Gleichgewicht liegt vor, wenn kein Spieler durch eine einseitige Abweichung davon eine höhere Auszahlung erzielen kann, d.h. für alle $a_i \in A_i$ und $i \in N$. Das Gefangenendilemma illustriert dies: Die dominante Strategie beider Spieler ist G, ABER: das Gleichgewicht (G, G) ist ineffizient, da (L, L) eine bessere Auszahlung ergeben würde. Ein weiteres Beispiel ist das Falke-und-Taube-Spiel. + +Zwei-Personen-Nullsummenspiele: Hier versuchen Spieler ihren Gewinn zu maximieren (und den Verlust des anderen zu minimieren); die Summe der Auszahlungen ist immer Null. Sie werden üblicherweise in Normalform (oder Matrixform) dargestellt. Konservative Strategien wählen den besten Wert aus allen ungünstigsten Fällen. Der Sattelpunkt (Schnittpunkt von Minimum und Maximum) repräsentiert die beste Strategie für beide Spieler. Kein Spieler kann sich durch einen einseitigen Strategiewechsel verbessern. Ein Beispiel ist die Schlacht in der Bismarcksee. Falls die Amerikaner die richtige Route überprüfen, kann die Bombardierung sofort begonnen werden (andernfalls verbleiben zwei Tage für die andere Route). Auf der Nordroute muss wegen schlechter Sicht die Bombardierung einen Tag ausbleiben. Dies ergibt folgendes 2PNS-Spiel (Gewinn ≡ mögliche Tage für Bombardierung): + +``` +SN12N32S +Japan (min)U.S. Air Force (max) +``` + +$\implies i^* = N$ und $U^- = 2 \implies j^* = N$ und $U^+ = 2 \implies$ Sattelpunkt bei (N, N). + + +Spielen in gemischten Strategien: Im Gegensatz zu reinen Strategien, bei denen sich ein Spieler *a priori* auf eine Strategie festlegt, werden hier Wahrscheinlichkeiten für verschiedene Strategien verwendet. Dies reduziert die Vorhersehbarkeit. Die Lösung findet man oft durch Multiplikation des 2PNS-Spiels mit Wahrscheinlichkeiten p und q und die Lösung der zugehörigen Gleichung (oftmals durch lineare Programmierung). Ein Beispiel: + +``` +1qq +S22S2131 +S11p15 +S121p +P1(min)P2(max) +``` + +$\implies$ Sattelpunkt der Funktion (graphisch) bei $p = 0.5$ und $q = 0.25$. + + +Checkpoints: Wichtige Begriffe sind Spiele in reinen Strategien, Darstellung in Normalform/Bimatrixform, dominante Strategie, Nash-Gleichgewicht und Zwei-Personen-Nullsummenspiele. + + +Gruppenentscheidungen: Gesucht ist eine kollektive Auswahlfunktion K für die Präferenz der Gesamtheit: $K: PA = PA \times PA \times \dots \times PA \to PA$. Wesentliche Bedingungen sind, dass die Auswahlfunktion total sein muss (für jede Kombination von Präferenzen muss es ein Ergebnis geben) und dass das Ergebnis selbst eine Relation in PA sein muss. Ein einfaches Verfahren ist die Rangaddition. Jede Individualpräferenz $\rho_i$ legt eine Rangabbildung fest. Die kollektive Präferenz wird aus den Rangnummern der Individuen bestimmt. Die Summe der Rangnummern ist keine Rangabbildung, kann aber korrigiert werden. Das Verfahren hat keine offensichtlichen Nachteile, aber kollektive Auswahlfunktionen $PA \to PA$ werden auch von unerwünschten Verfahren erfüllt. Zwei Bedingungen für „gerechte“ Verfahren sind die Pareto-Bedingung und die Unabhängigkeit von irrelevanten Alternativen. + + +--- + +## Game Theory Basics +Two-Person Zero-Sum Games +Bismarck Sea Battle +Mixed Strategies Game +Group Decisions +Preference Relations +Decision Procedures +Arrow's Impossibility Theorem + +# Spieltheorie: Eine Einführung + +## Zwei-Personen-Nullsummenspiele + +Zwei-Personen-Nullsummenspiele (2PNS-Spiele) werden auch als MinMax-Spiele bezeichnet. In Matrixform wird Spieler 1 zur Minimiererin (auch Zeilenspielerin genannt) und Spieler 2 zur Maximiererin (auch Spaltenspielerin genannt). Aufgrund der speziellen Struktur von 2PNS-Spielen nehmen Nash-Gleichgewichte die Form von Sattelpunkten an (Schnittpunkt von Minimum und Maximum). Der Min-Max-Theorem besagt: Ein Sattelpunkt existiert genau dann, wenn $U^- = a_{i*j*} = U^+$. Ein Beispiel hierfür ist die Schlacht in der Bismarcksee (2.-4. März 1943). Die Japaner wollten Truppen und Material von Rabaul (Bismarck-Archipel) nach Lae (Papua-Neuguinea) verlegen. Es standen zwei mögliche Routen zur Verfügung: eine regnerische Nordroute und eine sonnige Südroute. Der japanische Konvoi war unabhängig von der gewählten Route drei Tage unterwegs. Die U.S. Air Force wurde über die Verlegung informiert und wollte den Konvoi bombardieren, hatte aber nicht genügend Flugzeuge, um beide Routen gleichzeitig zu überwachen. Die Amerikaner mussten also eine Entscheidung treffen. Wenn beispielsweise $q$ in $S_{22}$ sowie $q$ in $S_{11}$ oder $S_{12}$ ist, ergibt sich $U^- = 2 = U^+$. + +## Möglichkeiten, Aktionen und Auszahlungsfunktionen + +Möglichkeiten/Aktionen: $A_1 = \{X, Y\}$ und $A_2 = \{X, Y\}$. Aktionsprofile: $A = \{(X, X), (X, Y), (Y, X), (Y, Y)\}$. Auszahlungsfunktion: $u_{1,2}: A \to (1, 0, 0, 1)$. Wie verändert sich das Spiel, wenn folgende Auszahlungsfunktionen gelten? Spieler 1: $u_1: A \to (1, 0, 0, 0)$; Spieler 2: $u_2: A \to (0, 0, 0, 1)$. + +## Spielen in gemischten Strategien + +Aufgabe: Steuerhinterziehung. Problem: Keine dominante Strategie/kein Nash-Gleichgewicht in reinen Strategien. Wie ändert sich die Situation für den Fall $s > 200$ Mio. CHF? + +$$ +\begin{array}{c|cc} + & K & S \\ +\hline +P & (200, 0) & (200 - s, 0.05 \cdot s - 10) \\ +S & (0, 0) & (0, -10) +\end{array} +$$ + + +## Gruppenentscheidungen: Relationen und Präferenzen + +Idee: Beschreibung von Beziehungen (z.B. Rangfolgen, Präferenzen) zwischen Elementen. Darstellung als Relation $R$ auf Paaren $(x, y)$ von Elementen aus $A$. Formal geschrieben als $(x, y) \in R$ oder abkürzend als $xRy$. Beispiel: Mit $R$ ist ‘$<$’ (kleiner) und $xRy$ folgt $x < y$. Rangabbildungen definieren eine Präferenzrelation: $x \rho y \iff r(x) < r(y)$. Relation $\rho$ ist transitiv und asymmetrisch. Die Menge aller (möglichen) darstellbaren Relationen auf $A$ ist definiert als $P_A := \{\rho \subset A \times A: \rho \text{ erfüllt } x \rho y \iff r(x) < r(y) \text{ für eine Rangabbildung } r\}$. Erweiterung der Relation durch alle Paare mit gleichem Rang: $x \rho^* y \iff r(x) \le r(y)$. Relation $\rho^*$ ist transitiv und reflexiv. Es gilt offensichtlich $\rho \subset \rho^*$. Analog zur Menge $P_A$ lässt sich damit definieren $P_A^* := \{\rho^* \subset A \times A: \rho^* \text{ erfüllt } x \rho^* y \iff r(x) \le r(y) \text{ für eine Rangabbildung } r\}$. Beispiel: $A = \{x, y, z\}$, $r(x) = 1$, $r(y) = r(z) = 2$. Zwischen beiden Relationen gilt folgende Beziehung: $x \rho y \iff \neg(y \rho^* x)$. Rangabbildungen sind vor allem für… + +## Gruppenentscheidungen: Entscheidungsverfahren + +Entscheidungsverfahren (Fortsetzung): Extrembeispiel 1: externer Diktator. Für beliebiges $\rho_E \in P_A$ gilt… Das Ergebnis ist offensichtlich eine Abbildung $P_A \to P_A$, aber unabhängig von den $\rho_i$. Das Verfahren kann kaum als „gerecht“ oder „demokratisch“ bezeichnet werden. Extrembeispiel 2: interner Diktator. Festlegung eines Individuums $d \in I$, dessen Präferenz $\rho_d$ das Ergebnis bestimmt. Damit gilt… Rangaddition: Beispiel: $I = \{1, 2\}$, $A = \{x, y, z\}$. Es folgt $x \rho y$, $y \rho z$ und $z \rho x$. Aus der Transitivität folgt schließlich $x \rho x \to$ kein gültiges Ergebnis, d.h. $\rho \notin P_A$. Einstimmigkeit: Element $x$ wird Element $y$ vorgezogen $\iff$ alle Individuen teilen diese Präferenz für alle. ABER: Ein einziges Individuum, das $y$… Die Rangfolge zwischen zwei Wahlmöglichkeiten kann nicht durch Präferenzänderungen der Individuen im Hinblick auf eine dritte Wahlmöglichkeit gekippt werden. Problem: Bereits mit diesen zwei Forderungen ist kein annähernd demokratisches Verfahren mehr möglich (Satz von Arrow). Quintessenz: Demokratie funktioniert nicht…? Bedingungen an Auswahlverfahren: Möglichkeit $z$ soll keinen Einfluss auf das Ergebnis haben. Oder formal: Wenn kein Individuum Präferenz $\rho_i, \rho_i' \in P_A$ bzgl. $x$ und $y$ ändert, soll gelten (für alle) $\implies$ (). Rangaddition somit als „gerechtes“ Verfahren ausgeschlossen. Condorcet-Verfahren und Einstimmigkeit liefern für $|A| > 2$ evtl. ein ungültiges Ergebnis. Nur interner Diktator $K_d$ erfüllt alle Bedingungen. Der Diktator ist „großzügiger“ gegenüber anderen Gruppenmitgliedern. Bei Indifferenz (es gilt weder $x \rho_d y$ noch $y \rho_d x$) sind beliebige Rangfolgen von $x, y$ zulässig. US-Wahlen haben nur zwei Kandidaten. + + +--- + +## Game Theory Introduction + +# Einführung in die Spieltheorie (CDS-1012) HS 2024 Prof. Dr. rer. nat. habil. Ralf-Peter Mundani DAViS + +**R.-P. Mundani, ICS, HS 2024 Einleitung** + +* Überblick +* Einführung in die Spieltheorie +* Modellierung und Definitionen +* Spiele in reinen Strategien +* Nash-Gleichgewicht und dominante Strategien +* Zwei-Personen-Nullsummenspiele +* Konservative Strategien +* Spiele in gemischten Strategien +* Berechnung von Sattelpunkten +* Mehrheitsbeschlüsse / Gruppenentscheidungen (→ Teil 2) + +**R.-P. Mundani, ICS, HS 2024 Motivation aus der Stochastik: Ziegenproblem (a.k.a. Monty-Hall-Dilemma)** + +* Spielshow: 3 Türen – zwei Nieten (Ziege) und ein Gewinn (Auto) +* SpielerIn wählt Tür, ShowmasterIn öffnet andere Tür (Ziege) Strategie: Wahl ändern…? +* Die anderen beiden Fälle (SpielerIn wählt Tür B oder C) sind symmetrisch +* In drei Fällen (3/9) Verlust, in sechs Fällen (6/9) Gewinn Wahl ändern  +* Quellen: depositphotos.com, jamara.com + +**R.-P. Mundani, ICS, HS 2024 Einführung in die Spieltheorie** + +* Grundlagen +* Definition: Spieltheorie +* Teilgebiet der Mathematik („Theorie des strategischen Denkens“) +* Modellierung von Entscheidungssituationen + +**R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien** + +* Gefangenendilemma (das wohl bekannteste nicht-kooperative Spiel in reinen Strategien) +* Hintergrund +* Zwei Gefangene werden beschuldigt, gemeinsam ein Verbrechen verübt zu haben +* Beide Gefangenen werden getrennt vernommen und können nicht kommunizieren +* Mangels konkreter Beweise kann beiden nur ein Teil der Tat nachgewiesen werden +* Mögliche Aktionen: + * Leugnen (L): niedrige Strafe (2 Jahre) + * Gestehen (G): hohe Strafe (5 Jahre) oder Höchststrafe (8 Jahre ➡️Kronzeugenregel) +* Maximierung der Auszahlung +* Funktion $u_i$ modelliert den Gewinn an Freiheit (also Höchststrafe – tatsächlicher Strafe) + +**R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien (Fortsetzung)** + +* Darstellung in Normalform (bei zwei SpielerInnen auch Bimatrixform genannt) +* Frage: Wie finden beide SpielerInnen die für sich beste Strategie…? + +$$ +\begin{array}{c|cc} + & G & L \\ +\hline +G & (6, 6) & (8, 0) \\ +L & (0, 8) & (3, 3) \\ +\end{array} +$$ + +$A_1 A_2 (u_1, u_2)$ + +**R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien** + +* Ein bisschen Mathematik  +* Nash-Gleichgewicht +* Ein Aktionsprofil ist ein Nash-Gleichgewicht, wenn kein Spieler durch einseitige Änderung seiner Strategie seinen Nutzen verbessern kann. +* Wenn eine Strategie $a_i$ für alle möglichen Strategien des Gegners einen höheren Nutzen liefert als eine andere Strategie $a_i$, dann wird von starker Dominanz gesprochen, d.h. Strategie $a_i$ ist stark dominant über Strategie $a_i$. +* Profile dominanter Strategien führen zu Nash-Gleichgewichten (Umkehrung nicht gültig). +* Nochmals Frage: Was bedeutet das für das Gefangenendilemma…? + +**R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien** + +* Ein bisschen Mathematik (Fortsetzung) +* Stark dominante Strategie beider SpielerInnen ist G + +**R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien** + +* Übung: Steuerhinterziehung (Fortsetzung) +* Zanoma, ein fiktives Unternehmen, hat im Jahr 2023 durch kreative Buchführung 200M CHF nicht versteuert. +* Wird Zanoma durch einen privaten Steuerprüfer die Steuerhinterziehung nachgewiesen, so muss das Unternehmen eine Nachzahlung von $s$ Millionen CHF (Steuer plus Busse) leisten. +* Steuerprüfer bekommt im Erfolgsfall 5% der Nachzahlung als Prämie. +* Steuerprüfer hat Strategien Prüfung (P; Kosten: 10M CHF) und keine Prüfung (KP; Kosten: 0). +* Zanoma hat Strategien Steuerhinterziehung (S) und keine Steuerhinterziehung (KS). +* Fragen: + * a) Wie sieht die dargestellte Situation als Spiel in Bimatrixform aus…? + * b) Gibt es für $s < 200$M CHF dominante Strategien und Nash-Gleichgewichte…? +* Jede Ähnlichkeit mit real existierenden Unternehmen ist rein zufällig. + + +**R.-P. Mundani, ICS, HS 2024 Spiele in reinen Strategien** + +* Konservative Strategien +* In Zwei-Personen-Nullsummenspielen (2PNS) wählt man Strategien, die im ungünstigsten Fall die beste Auszahlung garantieren. +* MinimiererIn P1: $U^- = \text{minimax}_{i,j} a_{ij}$ +* Für jede Strategie $i$: Maximiere über alle Spalten $j$ (➡️größtmöglicher Verlust) +* Wähle Minimum aller Spaltenmaxima ➡️konservative Strategie $i^*$ (Obergrenze $U^-$) +* MaximiererIn P2: $U^+ = \text{max}_j \text{min}_i a_{ij}$ +* Für jede Strategie $j$: Minimiere über alle Zeilen $i$ (➡️kleinstmöglicher Gewinn) +* Wähle Maximum aller Zeilenminima ➡️konservative Strategie $j^*$ (Untergrenze $U^+$) +* Minimax-Theorem: 2PNS-Spiele in gemischten Strategien haben immer einen Sattelpunkt. + +**R.-P. Mundani, ICS, HS 2024 Spiele in gemischten Strategien** + +* Einführung von Wahrscheinlichkeiten +* SpielerIn P1: Spielt mit Wahrscheinlichkeit $p$ Strategie $S_{11}$ und mit $1-p$ Strategie $S_{12}$. +* SpielerIn P2: Spielt mit Wahrscheinlichkeit $q$ Strategie $S_{21}$ und mit $1-q$ Strategie $S_{22}$. +* Für den Fall $n > 2$ Strategien: Wahrscheinlichkeiten $p_1, p_2, \dots, p_n$ je Strategie mit $\sum_{i=1}^n p_i = 1$. +* Bestimmung des Sattelpunkts +* Multiplizieren des 2PNS-Spiels mit $p$ und $q$: + +$$ +\begin{pmatrix} p & 1-p \end{pmatrix} +\begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} +\begin{pmatrix} q \\ 1-q \end{pmatrix} +$$ + +**R.-P. Mundani, ICS, HS 2024 Spiele in gemischten Strategien** + +* Nochmals Aufgabe: Steuerhinterziehung (Fortsetzung) +* Aus Sicht von Zanoma: $(200 - s) \cdot q + 200 \cdot (1 - q) = 0 \cdot q + 0 \cdot (1 - q) \iff q = 200/s$ +* Aus Sicht des Steuerprüfers: $(0.05 \cdot s - 10) \cdot p - 10 \cdot (1 - p) = 0 \cdot p + 0 \cdot (1 - p) \iff p = 200/s$ +* D.h. Nash-Gleichgewicht in gemischten Strategien somit für $p = q$, d.h. für $(200/s, 200/s)$. +* In anderen Worten: Höhere Nachzahlungen (Bussen) senken die Wahrscheinlichkeit einer Steuerprüfung. + +**R.-P. Mundani, ICS, HS 2024 Gruppenentscheidungen** + +* Satz von Arrow +* Mit $|A| > 2$ sei eine Menge von Auswahlmöglichkeiten. +* $K: P^A \to P^A$ sei kollektive Auswahlfunktion, die Pareto-Bedingung und Unabhängigkeit von irrelevanten Alternativen erfüllt. +* Dann existiert immer ein Diktator: Es gibt ein $d \in I$: Für alle $(\rho_1, \dots, \rho_n) \in P^A$: Für alle $(x, y) \in A \times A$: $x \rho_d y \implies x \rho y$. +* ABER: Diktator ist „großzügiger“ gegenüber anderen. + + +--- + +## Game Theory Basics + +Nicht-kooperative Spiele zeichnen sich dadurch aus, dass alle Spieler ihre Auszahlung maximieren, indem sie ihre beste Strategie wählen – basierend auf ihrem Wissen über die Strategien der anderen. Vereinbarungen zwischen den SpielerInnen gibt es keine. Im Gegensatz dazu stehen kooperative Spiele: Hier suchen alle SpielerInnen nach gemeinsamen Aktionen, die optimal für die Gruppe sind. Vor dem Spiel können Absprachen getroffen werden. Die Unterscheidung zwischen diesen beiden Spieltypen liegt also in der Möglichkeit zur Kooperation und der damit verbundenen Vereinbarung gemeinsamer Strategien. + + +--- + +## Game Theory Strategies + +Mixed Strategies Nash + +Group Decision Rankings + +Group Decision Preferences + +Group Decision Methods + +# Spieltheorie und Gruppenentscheidungen + +Vorhersehbarkeit durch Mitspieler*innen ist beispielsweise bei der Schere-Stein-Papier-Variante gegeben: Schere (100%), Stein (0%), Papier (0%). Gemischte Strategien hingegen entstehen, wenn eine Spielerin zufällig zwischen Strategien entscheidet. Formal wird dies durch eine Wahrscheinlichkeitsfunktion über (reinen) Strategien charakterisiert. Ein Beispiel wäre: Schere (25%), Stein (50%), Papier (25%). Reine Strategien führen jedoch häufig nicht zu Nash-Gleichgewichten. Nicht jedes Zwei-Personen-Nullsummenspiel (2PNS-Spiel) in reinen Strategien hat einen Sattelpunkt. Betrachten wir beispielsweise die folgende Auszahlungsmatrix: + +$$ +\begin{array}{c|cc} + & S_{21} & S_{22} \\ +\hline +S_{11} & 3 & -1 \\ +S_{12} & -1 & 5 \\ +\end{array} +$$ + +$\min_i \max_j \rightarrow i^* = S_{11}$ und $U^- = 3$, $\max_j \min_i \rightarrow j^* = S_{22}$ und $U^+ = 1$. Zwei-Personen-Nullsummenspiele in gemischten Strategien haben immer einen Sattelpunkt. Das Min-Max-Theorem wird hier erneut relevant. + + +Gruppenentscheidungen basieren auf Präferenzen und Entscheidungsverfahren. Situationen mit unerwünschten Ergebnissen sind jedoch in der Regel nicht vermeidbar. Wir betrachten eine endliche Menge $A$ von Möglichkeiten (z.B. Kandidat*innen, Pläne). Präferenzen entstehen durch die Vergabe von Rangnummern $r(x)$ mit $x \in A$. Für zwei Möglichkeiten $x, y \in A$ bedeutet $r(x) < r(y)$, dass $x$ gegenüber $y$ bevorzugt wird. Zwei (oder mehr) Möglichkeiten können dieselbe Rangnummer erhalten. Eine Rangabbildung $r: A \to P$ ist eine surjektive Abbildung der Menge der Möglichkeiten $A$ auf eine Menge von Präferenzen $P = \{1, \dots, k\} \subset \mathbb{N}$. Beispiel: + +$A = \{x, y, z\}$, $P = \{1, 2\}$ +$r(x) = 1$, $r(y) = r(z) = 2$. + +Eigenschaften von Relationen sind Transitivität (aus $xRy$ und $yRz$ folgt $xRz$), Reflexivität ($xRx$ für alle $x \in A$), und Asymmetrie ($xRy$ und $yRx$ gelten niemals gleichzeitig). Transitive und reflexive Relationen werden auch Quasiordnungen genannt. Rangabbildungen eignen sich besonders für Visualisierungen (z.B. Fußballtabellen). $x \rho y \iff \neg(y \rho^* x)$. + + +Betrachten wir nun Entscheidungsverfahren. Die Menge der Wähler*innen $I = \{1, \dots, n\}$ besteht aus Individuen, die von 1 bis n durchnummeriert sind. Jede Wählerin hat eine persönliche Präferenz $P_A$. Gesucht ist eine kollektive Auswahlfunktion. Die Mehrheitsentscheidung (Condorcet-Verfahren) vergleicht im direkten Vergleich zweier Elemente $x, y \in A$ die Individuen $\{i \in I: x \rho_i y\}$, die $x$ bevorzugen, mit den Individuen $\{i \in I: y \rho_i x\}$, die $y$ bevorzugen. Das Condorcet-Verfahren zählt, wer mehr Vergleiche gewinnt. Für zwei Elemente $x, y$ gilt: $K_C(\rho_1, \dots, \rho_n)$ ist eine Relation $\rho$. Das Verfahren ist für beliebige $\rho$ durchführbar, aber die Relation $\rho$ ist im Fall von mehr als zwei Möglichkeiten nicht immer transitiv, d.h. $\rho$ ist keine zulässige Präferenzrelation aus $P_A$. Beispiel: $I = \{1, 2, 3\}$, $A = \{x, y, z\}$. Es folgt $x \rho y$, $y \rho z$ und $z \rho x$. Aus der Transitivität würde $x \rho z$ folgen, was jedoch nicht der Fall ist. Ein einziges Individuum, das $y$ mindestens so schätzt wie $x$ ($y \le x$), führt dazu, dass das Verfahren in der Praxis keine echten Präferenzen liefert (für $|A| > 2$, $\rho \notin P_A$). + + +Bedingungen an Auswahlverfahren umfassen die Pareto-Bedingung (Einstimmigkeit). Eine kollektive Auswahlfunktion $K: P_A \to P_A$ erfüllt die Pareto-Bedingung, wenn für alle $\rho_i \in P_A$ gilt: Für alle $x, y \in A$, wenn $x \rho_i y$ für alle $i$, dann $x \rho y$. Der externe Diktator scheidet somit aus, alle anderen Verfahren erfüllen die Bedingung. Die Unabhängigkeit von irrelevanten Alternativen fordert, dass die Platzierung einer dritten Möglichkeit $z$ keinen Einfluss auf die Rangordnung zwischen $x$ und $y$ haben sollte. Eine Präferenzänderung bei einem Individuum kann die kollektive Präferenz beeinflussen. + + +--- + +## Game Theory Basics + +# Einführung in die Spieltheorie: Grundlagen und Anwendungen + +Die Spieltheorie modelliert Entscheidungssituationen, oft im Kontext sozialer Konflikte. Die zentrale Idee ist, dass der Erfolg nicht nur von den eigenen Entscheidungen, sondern auch von den Entscheidungen anderer abhängt. Wichtige Anwendungsgebiete finden sich in den Wirtschaftswissenschaften und der Ökonomie. Bekannte Vertreter sind John von Neumann und John Forbes Nash. Man unterscheidet zwischen kooperativen und nicht-kooperativen Spielen sowie simultanen und sequentiellen Spielen. + +Entscheidungen können unter Gewissheit getroffen werden (alle Aktionen und Konsequenzen sind bekannt), unter Risiko (bestimmte Wahrscheinlichkeiten von Aktionen und Konsequenzen sind bekannt) oder unter Unsicherheit (keine Aktionen und Konsequenzen sind bekannt). Die Modellierung umfasst eine Menge von SpielerInnen $N = \{1, 2, \dots, n\}$, eine Menge der Aktionen (auch Strategien genannt) $A_i$ von SpielerIn $i$ für alle $i \in N$, und eine Menge der Aktionsprofile (auch Strategieprofile) $A = \{(a_i)_{i \in N}, a_i \in A_i \text{ für alle } i \in N\}$. Die Auszahlungsfunktion $u_i: A \to \mathbb{R}$ beschreibt die Auszahlung für SpielerIn $i$. Ein Beispiel: Die Entscheidung zwischen zwei Möglichkeiten mit Aktionen $A_1 = \{X, Y\}$ und ... + +Ein Nash-Gleichgewicht liegt vor, wenn kein Spieler durch einseitige Abweichung von seiner Entscheidung eine höhere Auszahlung erzielen kann, während alle anderen SpielerInnen an ihren Entscheidungen festhalten. Formal: Ein Aktionsprofil bildet genau dann ein Nash-Gleichgewicht, wenn keine der SpielerInnen durch Abweichung davon eine höhere Auszahlung erhält. + +Zwei-Personen-Nullsummenspiele zeichnen sich dadurch aus, dass der Gewinn einer SpielerIn dem Verlust der anderen entspricht (die Summe ist immer null). Ein Beispiel ist das Spiel "Chicken" (Hühner-Spiel), bei dem zwei SpielerInnen (z.B. mit Autos) sich auf einer Kollisionskurs befinden und unabhängig voneinander entscheiden, ob sie ausweichen oder nicht (d.h. "hartnäckiges" Verhalten). Die USA nutzten dieses Spielmodell für strategische Analysen während der Kuba-Krise. Das Spiel wird interessant, wenn die Kosten für den Kampf ($C$) die Kosten bzw. den Wert des Sieges ($V$) überschreiten, d.h. $C > V > 0$. + +Als Übung betrachten wir Steuerhinterziehung. Ein Unternehmen, Zanoma, hat durch kreative Buchführung 200 Millionen CHF nicht versteuert. Falls $s < 200$, gilt $200 - s > 0$, und "S" (Steuerhinterziehung) ist eine strikt dominante Strategie. Falls $s < 200$, gilt $0.05 \cdot s - 10 < 0$, und "KP" (keine Prüfung) ist eine strikt dominante Strategie. + +Spiele in gemischten Strategien können alternativ als reine Strategien aus der jeweiligen Sicht von SpielerIn P1 und P2 berechnet werden. Ein Sattelpunkt beschreibt einen Gleichgewichtspunkt. Die Darstellung in Matrixform ermöglicht die Analyse konservativer Strategien (MinimiererIn/MaximiererIn). + +Gruppenentscheidungen befassen sich mit der Bestimmung einer Rangfolge von Möglichkeiten (z.B. Wahlen, Wettbewerbe). Nicht alle Beteiligten werden mit der Rangfolge einverstanden sein. Ein axiomatischer Ansatz stellt Eigenschaften auf und prüft, welche Entscheidungsverfahren diese erfüllen. Die Modellierung individueller Präferenzen und der Entscheidungsverfahren selbst ist zentral. Bedingungen an Auswahlverfahren beinhalten die Unabhängigkeit von irrelevanten Alternativen. + diff --git a/Code/pipeline-example/pyproject.toml b/Code/pipeline-example/pyproject.toml new file mode 100644 index 0000000..a0ed1af --- /dev/null +++ b/Code/pipeline-example/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "pipeline-example" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "dotenv>=0.9.9", + "google>=3.0.0", + "google-genai>=1.11.0", + "numpy>=2.2.4", + "pypdf>=5.4.0", + "scikit-learn>=1.6.1", + "sentence-transformers>=4.1.0", +] diff --git a/Code/pipeline-example/uv.lock b/Code/pipeline-example/uv.lock new file mode 100644 index 0000000..8e52bb8 --- /dev/null +++ b/Code/pipeline-example/uv.lock @@ -0,0 +1,941 @@ +version = 1 +revision = 1 +requires-python = ">=3.13" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285 }, +] + +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "dotenv" +version = "0.9.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dotenv" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892 }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "fsspec" +version = "2025.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/d8/8425e6ba5fcec61a1d16e41b1b71d2bf9344f1fe48012c2b48b9620feae5/fsspec-2025.3.2.tar.gz", hash = "sha256:e52c77ef398680bbd6a98c0e628fbc469491282981209907bbc8aea76a04fdc6", size = 299281 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/4b/e0cfc1a6f17e990f3e64b7d941ddc4acdc7b19d6edd51abf495f32b1a9e4/fsspec-2025.3.2-py3-none-any.whl", hash = "sha256:2daf8dc3d1dfa65b6aa37748d112773a7a08416f6c70d96b264c96476ecaf711", size = 194435 }, +] + +[[package]] +name = "google" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/97/b49c69893cddea912c7a660a4b6102c6b02cd268f8c7162dd70b7c16f753/google-3.0.0.tar.gz", hash = "sha256:143530122ee5130509ad5e989f0512f7cb218b2d4eddbafbad40fd10e8d8ccbe", size = 44978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/35/17c9141c4ae21e9a29a43acdfd848e3e468a810517f862cad07977bf8fe9/google-3.0.0-py2.py3-none-any.whl", hash = "sha256:889cf695f84e4ae2c55fbc0cfdaf4c1e729417fa52ab1db0485202ba173e4935", size = 45258 }, +] + +[[package]] +name = "google-auth" +version = "2.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/8e/8f45c9a32f73e786e954b8f9761c61422955d23c45d1e8c347f9b4b59e8e/google_auth-2.39.0.tar.gz", hash = "sha256:73222d43cdc35a3aeacbfdcaf73142a97839f10de930550d89ebfe1d0a00cde7", size = 274834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/12/ad37a1ef86006d0a0117fc06a4a00bd461c775356b534b425f00dde208ea/google_auth-2.39.0-py2.py3-none-any.whl", hash = "sha256:0150b6711e97fb9f52fe599f55648950cc4540015565d8fbb31be2ad6e1548a2", size = 212319 }, +] + +[[package]] +name = "google-genai" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "google-auth" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/44/64c6c23724580add879cbcca81ffed500955c1c21850468cd4dcf9c62a03/google_genai-1.11.0.tar.gz", hash = "sha256:0643b2f5373fbeae945d0cd5a37d157eab0c172bb5e14e905f2f8d45aa51cabb", size = 160955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/55f97203720cbda5a1c8e0460793914980e41c6ca4859fea735dd66d2c3a/google_genai-1.11.0-py3-none-any.whl", hash = "sha256:34fbe3c85419adbcddcb8222f99514596b3a69c80ff1a4ae30a01a763da27acc", size = 159687 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "httpcore" +version = "1.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.30.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/22/8eb91736b1dcb83d879bd49050a09df29a57cc5cd9f38e48a4b1c45ee890/huggingface_hub-0.30.2.tar.gz", hash = "sha256:9a7897c5b6fd9dad3168a794a8998d6378210f5b9688d0dfc180b1a228dc2466", size = 400868 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/27/1fb384a841e9661faad1c31cbfa62864f59632e876df5d795234da51c395/huggingface_hub-0.30.2-py3-none-any.whl", hash = "sha256:68ff05969927058cfa41df4f2155d4bb48f5f54f719dd0390103eefa9b191e28", size = 481433 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "joblib" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + +[[package]] +name = "numpy" +version = "2.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/78/31103410a57bc2c2b93a3597340a8119588571f6a4539067546cb9a0bfac/numpy-2.2.4.tar.gz", hash = "sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f", size = 20270701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/d0/bd5ad792e78017f5decfb2ecc947422a3669a34f775679a76317af671ffc/numpy-2.2.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cf4e5c6a278d620dee9ddeb487dc6a860f9b199eadeecc567f777daace1e9e7", size = 20933623 }, + { url = "https://files.pythonhosted.org/packages/c3/bc/2b3545766337b95409868f8e62053135bdc7fa2ce630aba983a2aa60b559/numpy-2.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1974afec0b479e50438fc3648974268f972e2d908ddb6d7fb634598cdb8260a0", size = 14148681 }, + { url = "https://files.pythonhosted.org/packages/6a/70/67b24d68a56551d43a6ec9fe8c5f91b526d4c1a46a6387b956bf2d64744e/numpy-2.2.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:79bd5f0a02aa16808fcbc79a9a376a147cc1045f7dfe44c6e7d53fa8b8a79392", size = 5148759 }, + { url = "https://files.pythonhosted.org/packages/1c/8b/e2fc8a75fcb7be12d90b31477c9356c0cbb44abce7ffb36be39a0017afad/numpy-2.2.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:3387dd7232804b341165cedcb90694565a6015433ee076c6754775e85d86f1fc", size = 6683092 }, + { url = "https://files.pythonhosted.org/packages/13/73/41b7b27f169ecf368b52533edb72e56a133f9e86256e809e169362553b49/numpy-2.2.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f527d8fdb0286fd2fd97a2a96c6be17ba4232da346931d967a0630050dfd298", size = 14081422 }, + { url = "https://files.pythonhosted.org/packages/4b/04/e208ff3ae3ddfbafc05910f89546382f15a3f10186b1f56bd99f159689c2/numpy-2.2.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce43e386c16898b91e162e5baaad90c4b06f9dcbe36282490032cec98dc8ae7", size = 16132202 }, + { url = "https://files.pythonhosted.org/packages/fe/bc/2218160574d862d5e55f803d88ddcad88beff94791f9c5f86d67bd8fbf1c/numpy-2.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31504f970f563d99f71a3512d0c01a645b692b12a63630d6aafa0939e52361e6", size = 15573131 }, + { url = "https://files.pythonhosted.org/packages/a5/78/97c775bc4f05abc8a8426436b7cb1be806a02a2994b195945600855e3a25/numpy-2.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:81413336ef121a6ba746892fad881a83351ee3e1e4011f52e97fba79233611fd", size = 17894270 }, + { url = "https://files.pythonhosted.org/packages/b9/eb/38c06217a5f6de27dcb41524ca95a44e395e6a1decdc0c99fec0832ce6ae/numpy-2.2.4-cp313-cp313-win32.whl", hash = "sha256:f486038e44caa08dbd97275a9a35a283a8f1d2f0ee60ac260a1790e76660833c", size = 6308141 }, + { url = "https://files.pythonhosted.org/packages/52/17/d0dd10ab6d125c6d11ffb6dfa3423c3571befab8358d4f85cd4471964fcd/numpy-2.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:207a2b8441cc8b6a2a78c9ddc64d00d20c303d79fba08c577752f080c4007ee3", size = 12636885 }, + { url = "https://files.pythonhosted.org/packages/fa/e2/793288ede17a0fdc921172916efb40f3cbc2aa97e76c5c84aba6dc7e8747/numpy-2.2.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8120575cb4882318c791f839a4fd66161a6fa46f3f0a5e613071aae35b5dd8f8", size = 20961829 }, + { url = "https://files.pythonhosted.org/packages/3a/75/bb4573f6c462afd1ea5cbedcc362fe3e9bdbcc57aefd37c681be1155fbaa/numpy-2.2.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a761ba0fa886a7bb33c6c8f6f20213735cb19642c580a931c625ee377ee8bd39", size = 14161419 }, + { url = "https://files.pythonhosted.org/packages/03/68/07b4cd01090ca46c7a336958b413cdbe75002286295f2addea767b7f16c9/numpy-2.2.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ac0280f1ba4a4bfff363a99a6aceed4f8e123f8a9b234c89140f5e894e452ecd", size = 5196414 }, + { url = "https://files.pythonhosted.org/packages/a5/fd/d4a29478d622fedff5c4b4b4cedfc37a00691079623c0575978d2446db9e/numpy-2.2.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:879cf3a9a2b53a4672a168c21375166171bc3932b7e21f622201811c43cdd3b0", size = 6709379 }, + { url = "https://files.pythonhosted.org/packages/41/78/96dddb75bb9be730b87c72f30ffdd62611aba234e4e460576a068c98eff6/numpy-2.2.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f05d4198c1bacc9124018109c5fba2f3201dbe7ab6e92ff100494f236209c960", size = 14051725 }, + { url = "https://files.pythonhosted.org/packages/00/06/5306b8199bffac2a29d9119c11f457f6c7d41115a335b78d3f86fad4dbe8/numpy-2.2.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f085ce2e813a50dfd0e01fbfc0c12bbe5d2063d99f8b29da30e544fb6483b8", size = 16101638 }, + { url = "https://files.pythonhosted.org/packages/fa/03/74c5b631ee1ded596945c12027649e6344614144369fd3ec1aaced782882/numpy-2.2.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:92bda934a791c01d6d9d8e038363c50918ef7c40601552a58ac84c9613a665bc", size = 15571717 }, + { url = "https://files.pythonhosted.org/packages/cb/dc/4fc7c0283abe0981e3b89f9b332a134e237dd476b0c018e1e21083310c31/numpy-2.2.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ee4d528022f4c5ff67332469e10efe06a267e32f4067dc76bb7e2cddf3cd25ff", size = 17879998 }, + { url = "https://files.pythonhosted.org/packages/e5/2b/878576190c5cfa29ed896b518cc516aecc7c98a919e20706c12480465f43/numpy-2.2.4-cp313-cp313t-win32.whl", hash = "sha256:05c076d531e9998e7e694c36e8b349969c56eadd2cdcd07242958489d79a7286", size = 6366896 }, + { url = "https://files.pythonhosted.org/packages/3e/05/eb7eec66b95cf697f08c754ef26c3549d03ebd682819f794cb039574a0a6/numpy-2.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d", size = 12739119 }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/a8/bcbb63b53a4b1234feeafb65544ee55495e1bb37ec31b999b963cbccfd1d/nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:df2c24502fd76ebafe7457dbc4716b2fec071aabaed4fb7691a201cde03704d9", size = 150057751 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pillow" +version = "11.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 }, + { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 }, + { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 }, + { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 }, + { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 }, + { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 }, + { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 }, + { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 }, + { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 }, + { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 }, + { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 }, + { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 }, + { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 }, + { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 }, + { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 }, + { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 }, + { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 }, + { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 }, + { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 }, + { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 }, + { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 }, + { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 }, +] + +[[package]] +name = "pipeline-example" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "dotenv" }, + { name = "google" }, + { name = "google-genai" }, + { name = "numpy" }, + { name = "pypdf" }, + { name = "scikit-learn" }, + { name = "sentence-transformers" }, +] + +[package.metadata] +requires-dist = [ + { name = "dotenv", specifier = ">=0.9.9" }, + { name = "google", specifier = ">=3.0.0" }, + { name = "google-genai", specifier = ">=1.11.0" }, + { name = "numpy", specifier = ">=2.2.4" }, + { name = "pypdf", specifier = ">=5.4.0" }, + { name = "scikit-learn", specifier = ">=1.6.1" }, + { name = "sentence-transformers", specifier = ">=4.1.0" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, +] + +[[package]] +name = "pydantic" +version = "2.11.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, + { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, + { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, + { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, + { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, + { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, + { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, + { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, + { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, + { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, + { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, + { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, + { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, + { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, + { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, + { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, + { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, +] + +[[package]] +name = "pypdf" +version = "5.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/43/4026f6ee056306d0e0eb04fcb9f2122a0f1a5c57ad9dc5e0d67399e47194/pypdf-5.4.0.tar.gz", hash = "sha256:9af476a9dc30fcb137659b0dec747ea94aa954933c52cf02ee33e39a16fe9175", size = 5012492 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/27/d83f8f2a03ca5408dc2cc84b49c0bf3fbf059398a6a2ea7c10acfe28859f/pypdf-5.4.0-py3-none-any.whl", hash = "sha256:db994ab47cadc81057ea1591b90e5b543e2b7ef2d0e31ef41a9bfe763c119dab", size = 302306 }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "scikit-learn" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, + { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, + { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, + { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, + { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, + { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, + { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, + { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, + { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, +] + +[[package]] +name = "scipy" +version = "1.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/b9/31ba9cd990e626574baf93fbc1ac61cf9ed54faafd04c479117517661637/scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec", size = 59417316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/40/09319f6e0f276ea2754196185f95cd191cb852288440ce035d5c3a931ea2/scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf", size = 38717587 }, + { url = "https://files.pythonhosted.org/packages/fe/c3/2854f40ecd19585d65afaef601e5e1f8dbf6758b2f95b5ea93d38655a2c6/scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37", size = 30100266 }, + { url = "https://files.pythonhosted.org/packages/dd/b1/f9fe6e3c828cb5930b5fe74cb479de5f3d66d682fa8adb77249acaf545b8/scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d", size = 22373768 }, + { url = "https://files.pythonhosted.org/packages/15/9d/a60db8c795700414c3f681908a2b911e031e024d93214f2d23c6dae174ab/scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb", size = 25154719 }, + { url = "https://files.pythonhosted.org/packages/37/3b/9bda92a85cd93f19f9ed90ade84aa1e51657e29988317fabdd44544f1dd4/scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27", size = 35163195 }, + { url = "https://files.pythonhosted.org/packages/03/5a/fc34bf1aa14dc7c0e701691fa8685f3faec80e57d816615e3625f28feb43/scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0", size = 37255404 }, + { url = "https://files.pythonhosted.org/packages/4a/71/472eac45440cee134c8a180dbe4c01b3ec247e0338b7c759e6cd71f199a7/scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32", size = 36860011 }, + { url = "https://files.pythonhosted.org/packages/01/b3/21f890f4f42daf20e4d3aaa18182dddb9192771cd47445aaae2e318f6738/scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d", size = 39657406 }, + { url = "https://files.pythonhosted.org/packages/0d/76/77cf2ac1f2a9cc00c073d49e1e16244e389dd88e2490c91d84e1e3e4d126/scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f", size = 40961243 }, + { url = "https://files.pythonhosted.org/packages/4c/4b/a57f8ddcf48e129e6054fa9899a2a86d1fc6b07a0e15c7eebff7ca94533f/scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9", size = 38870286 }, + { url = "https://files.pythonhosted.org/packages/0c/43/c304d69a56c91ad5f188c0714f6a97b9c1fed93128c691148621274a3a68/scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f", size = 30141634 }, + { url = "https://files.pythonhosted.org/packages/44/1a/6c21b45d2548eb73be9b9bff421aaaa7e85e22c1f9b3bc44b23485dfce0a/scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6", size = 22415179 }, + { url = "https://files.pythonhosted.org/packages/74/4b/aefac4bba80ef815b64f55da06f62f92be5d03b467f2ce3668071799429a/scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af", size = 25126412 }, + { url = "https://files.pythonhosted.org/packages/b1/53/1cbb148e6e8f1660aacd9f0a9dfa2b05e9ff1cb54b4386fe868477972ac2/scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274", size = 34952867 }, + { url = "https://files.pythonhosted.org/packages/2c/23/e0eb7f31a9c13cf2dca083828b97992dd22f8184c6ce4fec5deec0c81fcf/scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776", size = 36890009 }, + { url = "https://files.pythonhosted.org/packages/03/f3/e699e19cabe96bbac5189c04aaa970718f0105cff03d458dc5e2b6bd1e8c/scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828", size = 36545159 }, + { url = "https://files.pythonhosted.org/packages/af/f5/ab3838e56fe5cc22383d6fcf2336e48c8fe33e944b9037fbf6cbdf5a11f8/scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28", size = 39136566 }, + { url = "https://files.pythonhosted.org/packages/0a/c8/b3f566db71461cabd4b2d5b39bcc24a7e1c119535c8361f81426be39bb47/scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db", size = 40477705 }, +] + +[[package]] +name = "sentence-transformers" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "pillow" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/84/b30d1b29ff58cfdff423e36a50efd622c8e31d7039b1a0d5e72066620da1/sentence_transformers-4.1.0.tar.gz", hash = "sha256:f125ffd1c727533e0eca5d4567de72f84728de8f7482834de442fd90c2c3d50b", size = 272420 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/2d/1151b371f28caae565ad384fdc38198f1165571870217aedda230b9d7497/sentence_transformers-4.1.0-py3-none-any.whl", hash = "sha256:382a7f6be1244a100ce40495fb7523dbe8d71b3c10b299f81e6b735092b3b8ca", size = 345695 }, +] + +[[package]] +name = "setuptools" +version = "78.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "sympy" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, +] + +[[package]] +name = "torch" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/85/ead1349fc30fe5a32cadd947c91bda4a62fbfd7f8c34ee61f6398d38fb48/torch-2.6.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:4874a73507a300a5d089ceaff616a569e7bb7c613c56f37f63ec3ffac65259cf", size = 766626191 }, + { url = "https://files.pythonhosted.org/packages/dd/b0/26f06f9428b250d856f6d512413e9e800b78625f63801cbba13957432036/torch-2.6.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a0d5e1b9874c1a6c25556840ab8920569a7a4137afa8a63a32cee0bc7d89bd4b", size = 95611439 }, + { url = "https://files.pythonhosted.org/packages/c2/9c/fc5224e9770c83faed3a087112d73147cd7c7bfb7557dcf9ad87e1dda163/torch-2.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:510c73251bee9ba02ae1cb6c9d4ee0907b3ce6020e62784e2d7598e0cfa4d6cc", size = 204126475 }, + { url = "https://files.pythonhosted.org/packages/88/8b/d60c0491ab63634763be1537ad488694d316ddc4a20eaadd639cedc53971/torch-2.6.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:ff96f4038f8af9f7ec4231710ed4549da1bdebad95923953a25045dcf6fd87e2", size = 66536783 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.51.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/11/7414d5bc07690002ce4d7553602107bf969af85144bbd02830f9fb471236/transformers-4.51.3.tar.gz", hash = "sha256:e292fcab3990c6defe6328f0f7d2004283ca81a7a07b2de9a46d67fd81ea1409", size = 8941266 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/b6/5257d04ae327b44db31f15cce39e6020cc986333c715660b1315a9724d82/transformers-4.51.3-py3-none-any.whl", hash = "sha256:fd3279633ceb2b777013234bbf0b4f5c2d23c4626b05497691f00cfda55e8a83", size = 10383940 }, +] + +[[package]] +name = "triton" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/30/37a3384d1e2e9320331baca41e835e90a3767303642c7a80d4510152cbcf/triton-3.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5dfa23ba84541d7c0a531dfce76d8bcd19159d50a4a8b14ad01e91734a5c1b0", size = 253154278 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, +]